Remove CryptoKeySource
Closes gh-196
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,11 +15,20 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -27,16 +36,15 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.crypto.key.CryptoKeySource;
|
||||
import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
|
||||
import org.springframework.security.oauth2.jose.JoseHeader;
|
||||
import org.springframework.security.oauth2.jose.jws.NimbusJwsEncoder;
|
||||
import org.springframework.security.oauth2.jose.TestJwks;
|
||||
import org.springframework.security.oauth2.jwt.JoseHeader;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
||||
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwsEncoder;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
@@ -54,11 +62,6 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -90,7 +93,7 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
|
||||
private static RegisteredClientRepository registeredClientRepository;
|
||||
private static OAuth2AuthorizationService authorizationService;
|
||||
private static CryptoKeySource keySource;
|
||||
private static JWKSource<SecurityContext> jwkSource;
|
||||
private static NimbusJwsEncoder jwtEncoder;
|
||||
private static BiConsumer<JoseHeader.Builder, JwtClaimsSet.Builder> jwtCustomizer;
|
||||
|
||||
@@ -104,8 +107,9 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
public static void init() {
|
||||
registeredClientRepository = mock(RegisteredClientRepository.class);
|
||||
authorizationService = mock(OAuth2AuthorizationService.class);
|
||||
keySource = new StaticKeyGeneratingCryptoKeySource();
|
||||
jwtEncoder = new NimbusJwsEncoder(keySource);
|
||||
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
|
||||
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
||||
jwtEncoder = new NimbusJwsEncoder(jwkSource);
|
||||
jwtCustomizer = mock(BiConsumer.class);
|
||||
jwtEncoder.setJwtCustomizer(jwtCustomizer);
|
||||
}
|
||||
@@ -298,8 +302,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CryptoKeySource keySource() {
|
||||
return keySource;
|
||||
JWKSource<SecurityContext> jwkSource() {
|
||||
return jwkSource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,10 +15,18 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -26,10 +34,9 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.crypto.key.CryptoKeySource;
|
||||
import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.jose.TestJwks;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
@@ -38,10 +45,6 @@ import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenE
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -61,7 +64,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
public class OAuth2ClientCredentialsGrantTests {
|
||||
private static RegisteredClientRepository registeredClientRepository;
|
||||
private static OAuth2AuthorizationService authorizationService;
|
||||
private static CryptoKeySource keySource;
|
||||
private static JWKSource<SecurityContext> jwkSource;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
@@ -73,7 +76,8 @@ public class OAuth2ClientCredentialsGrantTests {
|
||||
public static void init() {
|
||||
registeredClientRepository = mock(RegisteredClientRepository.class);
|
||||
authorizationService = mock(OAuth2AuthorizationService.class);
|
||||
keySource = new StaticKeyGeneratingCryptoKeySource();
|
||||
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
|
||||
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -159,8 +163,8 @@ public class OAuth2ClientCredentialsGrantTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CryptoKeySource keySource() {
|
||||
return keySource;
|
||||
JWKSource<SecurityContext> jwkSource() {
|
||||
return jwkSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,10 +15,18 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -26,10 +34,9 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.crypto.key.CryptoKeySource;
|
||||
import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.jose.TestJwks;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
@@ -42,10 +49,6 @@ import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -67,7 +70,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
public class OAuth2RefreshTokenGrantTests {
|
||||
private static RegisteredClientRepository registeredClientRepository;
|
||||
private static OAuth2AuthorizationService authorizationService;
|
||||
private static CryptoKeySource keySource;
|
||||
private static JWKSource<SecurityContext> jwkSource;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
@@ -79,7 +82,8 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
public static void init() {
|
||||
registeredClientRepository = mock(RegisteredClientRepository.class);
|
||||
authorizationService = mock(OAuth2AuthorizationService.class);
|
||||
keySource = new StaticKeyGeneratingCryptoKeySource();
|
||||
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
|
||||
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -153,8 +157,8 @@ public class OAuth2RefreshTokenGrantTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CryptoKeySource keySource() {
|
||||
return keySource;
|
||||
JWKSource<SecurityContext> jwkSource() {
|
||||
return jwkSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,11 +15,19 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -27,12 +35,11 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.crypto.key.CryptoKeySource;
|
||||
import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource;
|
||||
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames2;
|
||||
import org.springframework.security.oauth2.jose.TestJwks;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
|
||||
@@ -46,10 +53,6 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -66,7 +69,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
public class OAuth2TokenRevocationTests {
|
||||
private static RegisteredClientRepository registeredClientRepository;
|
||||
private static OAuth2AuthorizationService authorizationService;
|
||||
private static CryptoKeySource keySource;
|
||||
private static JWKSource<SecurityContext> jwkSource;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
@@ -78,7 +81,8 @@ public class OAuth2TokenRevocationTests {
|
||||
public static void init() {
|
||||
registeredClientRepository = mock(RegisteredClientRepository.class);
|
||||
authorizationService = mock(OAuth2AuthorizationService.class);
|
||||
keySource = new StaticKeyGeneratingCryptoKeySource();
|
||||
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
|
||||
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -181,8 +185,8 @@ public class OAuth2TokenRevocationTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CryptoKeySource keySource() {
|
||||
return keySource;
|
||||
JWKSource<SecurityContext> jwkSource() {
|
||||
return jwkSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,11 +15,19 @@
|
||||
*/
|
||||
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
@@ -27,12 +35,11 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
|
||||
import org.springframework.security.config.test.SpringTestRule;
|
||||
import org.springframework.security.crypto.key.CryptoKeySource;
|
||||
import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.oauth2.jose.TestJwks;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.TokenType;
|
||||
@@ -40,20 +47,16 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
|
||||
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
|
||||
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
|
||||
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
|
||||
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
|
||||
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
@@ -80,7 +83,7 @@ public class OidcTests {
|
||||
private static final String issuerUrl = "https://example.com/issuer1";
|
||||
private static RegisteredClientRepository registeredClientRepository;
|
||||
private static OAuth2AuthorizationService authorizationService;
|
||||
private static CryptoKeySource keySource;
|
||||
private static JWKSource<SecurityContext> jwkSource;
|
||||
|
||||
@Rule
|
||||
public final SpringTestRule spring = new SpringTestRule();
|
||||
@@ -92,7 +95,8 @@ public class OidcTests {
|
||||
public static void init() {
|
||||
registeredClientRepository = mock(RegisteredClientRepository.class);
|
||||
authorizationService = mock(OAuth2AuthorizationService.class);
|
||||
keySource = new StaticKeyGeneratingCryptoKeySource();
|
||||
JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK);
|
||||
jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -224,8 +228,8 @@ public class OidcTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CryptoKeySource keySource() {
|
||||
return keySource;
|
||||
JWKSource<SecurityContext> jwkSource() {
|
||||
return jwkSource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.crypto.key;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.KeyPair;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.springframework.security.crypto.key.KeyGeneratorUtils.generateRsaKey;
|
||||
import static org.springframework.security.crypto.key.KeyGeneratorUtils.generateSecretKey;
|
||||
|
||||
/**
|
||||
* Tests for {@link CryptoKey}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class CryptoKeyTests {
|
||||
private static SecretKey secretKey;
|
||||
private static KeyPair rsaKeyPair;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() {
|
||||
secretKey = generateSecretKey();
|
||||
rsaKeyPair = generateRsaKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void symmetricWhenSecretKeyNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> CryptoKey.symmetric(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("key cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataWhenNameNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> CryptoKey.symmetric(secretKey).metadata(null, "value"))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("name cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataWhenValueNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> CryptoKey.symmetric(secretKey).metadata("name", null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("value cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void symmetricWhenAllAttributesProvidedThenAllAttributesAreSet() {
|
||||
Map<String, Object> keyMetadata = new HashMap<>();
|
||||
keyMetadata.put("name1", "value1");
|
||||
keyMetadata.put("name2", "value2");
|
||||
|
||||
SymmetricKey symmetricKey = CryptoKey.symmetric(secretKey)
|
||||
.id("id")
|
||||
.metadata(metadata -> metadata.putAll(keyMetadata))
|
||||
.build();
|
||||
|
||||
assertThat(symmetricKey.getKey()).isEqualTo(secretKey);
|
||||
assertThat(symmetricKey.getId()).isEqualTo("id");
|
||||
assertThat(symmetricKey.getMetadata()).isEqualTo(keyMetadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asymmetricWhenPrivateKeyNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> CryptoKey.asymmetric(null, rsaKeyPair.getPublic()))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("key cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asymmetricWhenPublicKeyNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> CryptoKey.asymmetric(rsaKeyPair.getPrivate(), null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("publicKey cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asymmetricWhenAllAttributesProvidedThenAllAttributesAreSet() {
|
||||
Map<String, Object> keyMetadata = new HashMap<>();
|
||||
keyMetadata.put("name1", "value1");
|
||||
keyMetadata.put("name2", "value2");
|
||||
|
||||
AsymmetricKey asymmetricKey = CryptoKey.asymmetric(rsaKeyPair.getPrivate(), rsaKeyPair.getPublic())
|
||||
.id("id")
|
||||
.metadata(metadata -> metadata.putAll(keyMetadata))
|
||||
.build();
|
||||
|
||||
assertThat(asymmetricKey.getKey()).isEqualTo(rsaKeyPair.getPrivate());
|
||||
assertThat(asymmetricKey.getPublicKey()).isEqualTo(rsaKeyPair.getPublic());
|
||||
assertThat(asymmetricKey.getId()).isEqualTo("id");
|
||||
assertThat(asymmetricKey.getMetadata()).isEqualTo(keyMetadata);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.crypto.key;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
import static org.springframework.security.crypto.key.KeyGeneratorUtils.generateEcKey;
|
||||
import static org.springframework.security.crypto.key.KeyGeneratorUtils.generateRsaKey;
|
||||
import static org.springframework.security.crypto.key.KeyGeneratorUtils.generateSecretKey;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class TestCryptoKeys {
|
||||
|
||||
public static SymmetricKey.Builder secretKey() {
|
||||
return CryptoKey.symmetric(generateSecretKey());
|
||||
}
|
||||
|
||||
public static AsymmetricKey.Builder rsaKey() {
|
||||
KeyPair rsaKeyPair = generateRsaKey();
|
||||
return CryptoKey.asymmetric(rsaKeyPair.getPrivate(), rsaKeyPair.getPublic());
|
||||
}
|
||||
|
||||
public static AsymmetricKey.Builder ecKey() {
|
||||
KeyPair ecKeyPair = generateEcKey();
|
||||
return CryptoKey.asymmetric(ecKeyPair.getPrivate(), ecKeyPair.getPublic());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.jose;
|
||||
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import com.nimbusds.jose.jwk.Curve;
|
||||
import com.nimbusds.jose.jwk.ECKey;
|
||||
import com.nimbusds.jose.jwk.KeyUse;
|
||||
import com.nimbusds.jose.jwk.OctetSequenceKey;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public final class TestJwks {
|
||||
|
||||
// @formatter:off
|
||||
public static final RSAKey DEFAULT_RSA_JWK =
|
||||
jwk(
|
||||
TestKeys.DEFAULT_PUBLIC_KEY,
|
||||
TestKeys.DEFAULT_PRIVATE_KEY
|
||||
).build();
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
public static final ECKey DEFAULT_EC_JWK =
|
||||
jwk(
|
||||
(ECPublicKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPublic(),
|
||||
(ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate()
|
||||
).build();
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
public static final OctetSequenceKey DEFAULT_SECRET_JWK =
|
||||
jwk(
|
||||
TestKeys.DEFAULT_SECRET_KEY
|
||||
).build();
|
||||
// @formatter:on
|
||||
|
||||
private TestJwks() {
|
||||
}
|
||||
|
||||
public static RSAKey.Builder jwk(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
|
||||
// @formatter:off
|
||||
return new RSAKey.Builder(publicKey)
|
||||
.privateKey(privateKey)
|
||||
.keyUse(KeyUse.SIGNATURE)
|
||||
.keyID("rsa-jwk-kid");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
public static ECKey.Builder jwk(ECPublicKey publicKey, ECPrivateKey privateKey) {
|
||||
// @formatter:off
|
||||
Curve curve = Curve.forECParameterSpec(publicKey.getParams());
|
||||
return new ECKey.Builder(curve, publicKey)
|
||||
.privateKey(privateKey)
|
||||
.keyUse(KeyUse.SIGNATURE)
|
||||
.keyID("ec-jwk-kid");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
public static OctetSequenceKey.Builder jwk(SecretKey secretKey) {
|
||||
// @formatter:off
|
||||
return new OctetSequenceKey.Builder(secretKey)
|
||||
.keyUse(KeyUse.SIGNATURE)
|
||||
.keyID("secret-jwk-kid");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.oauth2.jose;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.ECFieldFp;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.EllipticCurve;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* This class is a straight copy from Spring Security.
|
||||
* It should be removed when merging this codebase into Spring Security.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.2
|
||||
*/
|
||||
public final class TestKeys {
|
||||
|
||||
public static final KeyFactory kf;
|
||||
static {
|
||||
try {
|
||||
kf = KeyFactory.getInstance("RSA");
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
public static final String DEFAULT_ENCODED_SECRET_KEY = "bCzY/M48bbkwBEWjmNSIEPfwApcvXOnkCxORBEbPr+4=";
|
||||
|
||||
public static final SecretKey DEFAULT_SECRET_KEY = new SecretKeySpec(
|
||||
Base64.getDecoder().decode(DEFAULT_ENCODED_SECRET_KEY), "AES");
|
||||
|
||||
// @formatter:off
|
||||
public static final String DEFAULT_RSA_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd"
|
||||
+ "7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv"
|
||||
+ "c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6"
|
||||
+ "iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2"
|
||||
+ "kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o"
|
||||
+ "RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj"
|
||||
+ "KwIDAQAB";
|
||||
// @formatter:on
|
||||
|
||||
public static final RSAPublicKey DEFAULT_PUBLIC_KEY;
|
||||
static {
|
||||
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PUBLIC_KEY));
|
||||
try {
|
||||
DEFAULT_PUBLIC_KEY = (RSAPublicKey) kf.generatePublic(spec);
|
||||
}
|
||||
catch (InvalidKeySpecException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
public static final String DEFAULT_RSA_PRIVATE_KEY = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA"
|
||||
+ "iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM"
|
||||
+ "g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK"
|
||||
+ "LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF"
|
||||
+ "oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc"
|
||||
+ "3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn"
|
||||
+ "+jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE"
|
||||
+ "E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek"
|
||||
+ "lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG"
|
||||
+ "mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7"
|
||||
+ "62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0"
|
||||
+ "bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA"
|
||||
+ "+Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH"
|
||||
+ "Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA"
|
||||
+ "8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd"
|
||||
+ "I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY"
|
||||
+ "QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d"
|
||||
+ "rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk"
|
||||
+ "HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA"
|
||||
+ "Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN"
|
||||
+ "HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a"
|
||||
+ "FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF"
|
||||
+ "snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H"
|
||||
+ "c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM"
|
||||
+ "TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR"
|
||||
+ "47jndeyIaMTNETEmOnms+as17g==";
|
||||
// @formatter:on
|
||||
|
||||
public static final RSAPrivateKey DEFAULT_PRIVATE_KEY;
|
||||
static {
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(DEFAULT_RSA_PRIVATE_KEY));
|
||||
try {
|
||||
DEFAULT_PRIVATE_KEY = (RSAPrivateKey) kf.generatePrivate(spec);
|
||||
}
|
||||
catch (InvalidKeySpecException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static final KeyPair DEFAULT_RSA_KEY_PAIR = new KeyPair(DEFAULT_PUBLIC_KEY, DEFAULT_PRIVATE_KEY);
|
||||
|
||||
public static final KeyPair DEFAULT_EC_KEY_PAIR = generateEcKeyPair();
|
||||
|
||||
static KeyPair generateEcKeyPair() {
|
||||
EllipticCurve ellipticCurve = new EllipticCurve(
|
||||
new ECFieldFp(new BigInteger(
|
||||
"115792089210356248762697446949407573530086143415290314195533631308867097853951")),
|
||||
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
|
||||
new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291"));
|
||||
ECPoint ecPoint = new ECPoint(
|
||||
new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
|
||||
new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109"));
|
||||
ECParameterSpec ecParameterSpec = new ECParameterSpec(ellipticCurve, ecPoint,
|
||||
new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"), 1);
|
||||
|
||||
KeyPair keyPair;
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
|
||||
keyPairGenerator.initialize(ecParameterSpec);
|
||||
keyPair = keyPairGenerator.generateKeyPair();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
private TestKeys() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.jose.jws;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.crypto.key.AsymmetricKey;
|
||||
import org.springframework.security.crypto.key.CryptoKeySource;
|
||||
import org.springframework.security.crypto.key.TestCryptoKeys;
|
||||
import org.springframework.security.oauth2.jose.JoseHeader;
|
||||
import org.springframework.security.oauth2.jose.JoseHeaderNames;
|
||||
import org.springframework.security.oauth2.jose.TestJoseHeaders;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
||||
import org.springframework.security.oauth2.jwt.JwtEncodingException;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.TestJwtClaimsSets;
|
||||
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link NimbusJwsEncoder}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class NimbusJwsEncoderTests {
|
||||
private CryptoKeySource keySource;
|
||||
private NimbusJwsEncoder jwtEncoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.keySource = mock(CryptoKeySource.class);
|
||||
this.jwtEncoder = new NimbusJwsEncoder(this.keySource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenKeySourceNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new NimbusJwsEncoder(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("keySource cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setJwtCustomizerWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> this.jwtEncoder.setJwtCustomizer(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("jwtCustomizer cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenHeadersNullThenThrowIllegalArgumentException() {
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatThrownBy(() -> this.jwtEncoder.encode(null, jwtClaimsSet))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("headers cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenClaimsNullThenThrowIllegalArgumentException() {
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
|
||||
assertThatThrownBy(() -> this.jwtEncoder.encode(joseHeader, null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("claims cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenUnsupportedKeyThenThrowJwtEncodingException() {
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatThrownBy(() -> this.jwtEncoder.encode(joseHeader, jwtClaimsSet))
|
||||
.isInstanceOf(JwtEncodingException.class)
|
||||
.hasMessageContaining("Unsupported key for algorithm 'RS256'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenUnsupportedKeyAlgorithmThenThrowJwtEncodingException() {
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader(SignatureAlgorithm.ES256).build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatThrownBy(() -> this.jwtEncoder.encode(joseHeader, jwtClaimsSet))
|
||||
.isInstanceOf(JwtEncodingException.class)
|
||||
.hasMessageContaining("Unsupported key for algorithm 'ES256'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenUnsupportedKeyTypeThenThrowJwtEncodingException() {
|
||||
AsymmetricKey ecKey = TestCryptoKeys.ecKey().build();
|
||||
when(this.keySource.getKeys()).thenReturn(Collections.singleton(ecKey));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader(SignatureAlgorithm.ES256).build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatThrownBy(() -> this.jwtEncoder.encode(joseHeader, jwtClaimsSet))
|
||||
.isInstanceOf(JwtEncodingException.class)
|
||||
.hasMessageContaining("Unsupported key type 'EC'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenSuccessThenDecodes() {
|
||||
AsymmetricKey rsaKey = TestCryptoKeys.rsaKey().build();
|
||||
when(this.keySource.getKeys()).thenReturn(Collections.singleton(rsaKey));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader()
|
||||
.headers(headers -> headers.remove(JoseHeaderNames.CRIT))
|
||||
.build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
Jwt jws = this.jwtEncoder.encode(joseHeader, jwtClaimsSet);
|
||||
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey((RSAPublicKey) rsaKey.getPublicKey()).build();
|
||||
jwtDecoder.decode(jws.getTokenValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenCustomizerSetThenCalled() {
|
||||
AsymmetricKey rsaKey = TestCryptoKeys.rsaKey().build();
|
||||
when(this.keySource.getKeys()).thenReturn(Collections.singleton(rsaKey));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader()
|
||||
.headers(headers -> headers.remove(JoseHeaderNames.CRIT))
|
||||
.build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
BiConsumer<JoseHeader.Builder, JwtClaimsSet.Builder> jwtCustomizer = mock(BiConsumer.class);
|
||||
this.jwtEncoder.setJwtCustomizer(jwtCustomizer);
|
||||
|
||||
this.jwtEncoder.encode(joseHeader, jwtClaimsSet);
|
||||
|
||||
verify(jwtCustomizer).accept(any(JoseHeader.Builder.class), any(JwtClaimsSet.Builder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenMultipleActiveKeysThenUseFirst() {
|
||||
AsymmetricKey rsaKey1 = TestCryptoKeys.rsaKey().build();
|
||||
AsymmetricKey rsaKey2 = TestCryptoKeys.rsaKey().build();
|
||||
when(this.keySource.getKeys()).thenReturn(
|
||||
Stream.of(rsaKey1, rsaKey2)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader()
|
||||
.headers(headers -> headers.remove(JoseHeaderNames.CRIT))
|
||||
.build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
Jwt jws = this.jwtEncoder.encode(joseHeader, jwtClaimsSet);
|
||||
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey((RSAPublicKey) rsaKey1.getPublicKey()).build();
|
||||
jwtDecoder.decode(jws.getTokenValue());
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.jose;
|
||||
package org.springframework.security.oauth2.jwt;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -40,14 +41,13 @@ public class JoseHeaderTests {
|
||||
JoseHeader expectedJoseHeader = TestJoseHeaders.joseHeader().build();
|
||||
|
||||
JoseHeader joseHeader = JoseHeader.withAlgorithm(expectedJoseHeader.getJwsAlgorithm())
|
||||
.jwkSetUri(expectedJoseHeader.getJwkSetUri())
|
||||
.jwkSetUri(expectedJoseHeader.getJwkSetUri().toExternalForm())
|
||||
.jwk(expectedJoseHeader.getJwk())
|
||||
.keyId(expectedJoseHeader.getKeyId())
|
||||
.x509Uri(expectedJoseHeader.getX509Uri())
|
||||
.x509Uri(expectedJoseHeader.getX509Uri().toExternalForm())
|
||||
.x509CertificateChain(expectedJoseHeader.getX509CertificateChain())
|
||||
.x509SHA1Thumbprint(expectedJoseHeader.getX509SHA1Thumbprint())
|
||||
.x509SHA256Thumbprint(expectedJoseHeader.getX509SHA256Thumbprint())
|
||||
.critical(expectedJoseHeader.getCritical())
|
||||
.type(expectedJoseHeader.getType())
|
||||
.contentType(expectedJoseHeader.getContentType())
|
||||
.headers(headers -> headers.put("custom-header-name", "custom-header-value"))
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.jwt;
|
||||
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import com.nimbusds.jose.KeySourceException;
|
||||
import com.nimbusds.jose.jwk.ECKey;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.JWKSelector;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.KeyUse;
|
||||
import com.nimbusds.jose.jwk.OctetSequenceKey;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.springframework.security.oauth2.jose.TestJwks;
|
||||
import org.springframework.security.oauth2.jose.TestKeys;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link NimbusJwsEncoder}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class NimbusJwsEncoderTests {
|
||||
|
||||
private JWKSource<SecurityContext> jwkSource;
|
||||
|
||||
private NimbusJwsEncoder jwsEncoder;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.jwkSource = mock(JWKSource.class);
|
||||
this.jwsEncoder = new NimbusJwsEncoder(this.jwkSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new NimbusJwsEncoder(null))
|
||||
.withMessage("jwkSource cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setJwtCustomizerWhenNullThenThrowIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.jwsEncoder.setJwtCustomizer(null))
|
||||
.withMessage("jwtCustomizer cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenHeadersNullThenThrowIllegalArgumentException() {
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.jwsEncoder.encode(null, jwtClaimsSet))
|
||||
.withMessage("headers cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenClaimsNullThenThrowIllegalArgumentException() {
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.jwsEncoder.encode(joseHeader, null))
|
||||
.withMessage("claims cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenCustomizerSetThenCalled() throws Exception {
|
||||
RSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;
|
||||
given(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));
|
||||
|
||||
BiConsumer<JoseHeader.Builder, JwtClaimsSet.Builder> jwtCustomizer = mock(BiConsumer.class);
|
||||
this.jwsEncoder.setJwtCustomizer(jwtCustomizer);
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
this.jwsEncoder.encode(joseHeader, jwtClaimsSet);
|
||||
|
||||
verify(jwtCustomizer).accept(any(JoseHeader.Builder.class), any(JwtClaimsSet.Builder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenJwkSelectFailedThenThrowJwtEncodingException() throws Exception {
|
||||
given(this.jwkSource.get(any(), any())).willThrow(new KeySourceException("key source error"));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatExceptionOfType(JwtEncodingException.class)
|
||||
.isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet))
|
||||
.withMessageContaining("Failed to select a JWK signing key -> key source error");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenJwkMultipleSelectedThenThrowJwtEncodingException() throws Exception {
|
||||
RSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;
|
||||
given(this.jwkSource.get(any(), any())).willReturn(Arrays.asList(rsaJwk, rsaJwk));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatExceptionOfType(JwtEncodingException.class)
|
||||
.isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet))
|
||||
.withMessageContaining("Found multiple JWK signing keys for algorithm 'RS256'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenJwkSelectEmptyThenThrowJwtEncodingException() {
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatExceptionOfType(JwtEncodingException.class)
|
||||
.isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet))
|
||||
.withMessageContaining("Failed to select a JWK signing key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenJwkKidNullThenThrowJwtEncodingException() throws Exception {
|
||||
// @formatter:off
|
||||
RSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)
|
||||
.keyID(null)
|
||||
.build();
|
||||
// @formatter:on
|
||||
|
||||
given(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatExceptionOfType(JwtEncodingException.class)
|
||||
.isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet))
|
||||
.withMessageContaining("The \"kid\" (key ID) from the selected JWK cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenJwkUseEncryptionThenThrowJwtEncodingException() throws Exception {
|
||||
// @formatter:off
|
||||
RSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)
|
||||
.keyUse(KeyUse.ENCRYPTION)
|
||||
.build();
|
||||
// @formatter:on
|
||||
|
||||
given(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
assertThatExceptionOfType(JwtEncodingException.class)
|
||||
.isThrownBy(() -> this.jwsEncoder.encode(joseHeader, jwtClaimsSet)).withMessageContaining(
|
||||
"Failed to create a JWS Signer -> The JWK use must be sig (signature) or unspecified");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenSuccessThenDecodes() throws Exception {
|
||||
RSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;
|
||||
given(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(rsaJwk));
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
Jwt encodedJws = this.jwsEncoder.encode(joseHeader, jwtClaimsSet);
|
||||
|
||||
// Assert headers/claims were added
|
||||
assertThat(encodedJws.getHeaders().get(JoseHeaderNames.TYP)).isEqualTo("JWT");
|
||||
assertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk.getKeyID());
|
||||
assertThat(encodedJws.getId()).isNotNull();
|
||||
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk.toRSAPublicKey()).build();
|
||||
jwtDecoder.decode(encodedJws.getTokenValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeWhenKeysRotatedThenNewKeyUsed() throws Exception {
|
||||
TestJWKSource jwkSource = new TestJWKSource();
|
||||
JWKSource<SecurityContext> jwkSourceDelegate = spy(new JWKSource<SecurityContext>() {
|
||||
@Override
|
||||
public List<JWK> get(JWKSelector jwkSelector, SecurityContext context) {
|
||||
return jwkSource.get(jwkSelector, context);
|
||||
}
|
||||
});
|
||||
NimbusJwsEncoder jwsEncoder = new NimbusJwsEncoder(jwkSourceDelegate);
|
||||
|
||||
JwkListResultCaptor jwkListResultCaptor = new JwkListResultCaptor();
|
||||
willAnswer(jwkListResultCaptor).given(jwkSourceDelegate).get(any(), any());
|
||||
|
||||
JoseHeader joseHeader = TestJoseHeaders.joseHeader().build();
|
||||
JwtClaimsSet jwtClaimsSet = TestJwtClaimsSets.jwtClaimsSet().build();
|
||||
|
||||
Jwt encodedJws = jwsEncoder.encode(joseHeader, jwtClaimsSet);
|
||||
|
||||
JWK jwk1 = jwkListResultCaptor.getResult().get(0);
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(((RSAKey) jwk1).toRSAPublicKey()).build();
|
||||
jwtDecoder.decode(encodedJws.getTokenValue());
|
||||
|
||||
jwkSource.rotate(); // Trigger key rotation
|
||||
|
||||
encodedJws = jwsEncoder.encode(joseHeader, jwtClaimsSet);
|
||||
|
||||
JWK jwk2 = jwkListResultCaptor.getResult().get(0);
|
||||
jwtDecoder = NimbusJwtDecoder.withPublicKey(((RSAKey) jwk2).toRSAPublicKey()).build();
|
||||
jwtDecoder.decode(encodedJws.getTokenValue());
|
||||
|
||||
assertThat(jwk1.getKeyID()).isNotEqualTo(jwk2.getKeyID());
|
||||
}
|
||||
|
||||
private static final class JwkListResultCaptor implements Answer<List<JWK>> {
|
||||
|
||||
private List<JWK> result;
|
||||
|
||||
private List<JWK> getResult() {
|
||||
return this.result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<JWK> answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
this.result = (List<JWK>) invocationOnMock.callRealMethod();
|
||||
return this.result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class TestJWKSource implements JWKSource<SecurityContext> {
|
||||
|
||||
private int keyId = 1000;
|
||||
|
||||
private JWKSet jwkSet;
|
||||
|
||||
private TestJWKSource() {
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JWK> get(JWKSelector jwkSelector, SecurityContext context) {
|
||||
return jwkSelector.select(this.jwkSet);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// @formatter:off
|
||||
RSAKey rsaJwk = TestJwks.jwk(TestKeys.DEFAULT_PUBLIC_KEY, TestKeys.DEFAULT_PRIVATE_KEY)
|
||||
.keyID("rsa-jwk-" + this.keyId++)
|
||||
.build();
|
||||
ECKey ecJwk = TestJwks.jwk((ECPublicKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPublic(), (ECPrivateKey) TestKeys.DEFAULT_EC_KEY_PAIR.getPrivate())
|
||||
.keyID("ec-jwk-" + this.keyId++)
|
||||
.build();
|
||||
OctetSequenceKey secretJwk = TestJwks.jwk(TestKeys.DEFAULT_SECRET_KEY)
|
||||
.keyID("secret-jwk-" + this.keyId++)
|
||||
.build();
|
||||
// @formatter:on
|
||||
this.jwkSet = new JWKSet(Arrays.asList(rsaJwk, ecJwk, secretJwk));
|
||||
}
|
||||
|
||||
private void rotate() {
|
||||
init();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,38 +13,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.oauth2.jose;
|
||||
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
package org.springframework.security.oauth2.jwt;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class TestJoseHeaders {
|
||||
public final class TestJoseHeaders {
|
||||
|
||||
private TestJoseHeaders() {
|
||||
}
|
||||
|
||||
public static JoseHeader.Builder joseHeader() {
|
||||
return joseHeader(SignatureAlgorithm.RS256);
|
||||
}
|
||||
|
||||
public static JoseHeader.Builder joseHeader(SignatureAlgorithm signatureAlgorithm) {
|
||||
// @formatter:off
|
||||
return JoseHeader.withAlgorithm(signatureAlgorithm)
|
||||
.jwkSetUri("https://provider.com/oauth2/jwks")
|
||||
.jwk(rsaJwk())
|
||||
.keyId(UUID.randomUUID().toString())
|
||||
.keyId("keyId")
|
||||
.x509Uri("https://provider.com/oauth2/x509")
|
||||
.x509CertificateChain(Arrays.asList("x509Cert1", "x509Cert2"))
|
||||
.x509SHA1Thumbprint("x509SHA1Thumbprint")
|
||||
.x509SHA256Thumbprint("x509SHA256Thumbprint")
|
||||
.critical(Collections.singleton("custom-header-name"))
|
||||
.type("JWT")
|
||||
.contentType("jwt-content-type")
|
||||
.header("custom-header-name", "custom-header-value");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static Map<String, Object> rsaJwk() {
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,18 +18,21 @@ package org.springframework.security.oauth2.jwt;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class TestJwtClaimsSets {
|
||||
public final class TestJwtClaimsSets {
|
||||
|
||||
private TestJwtClaimsSets() {
|
||||
}
|
||||
|
||||
public static JwtClaimsSet.Builder jwtClaimsSet() {
|
||||
String issuer = "https://provider.com";
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS);
|
||||
|
||||
// @formatter:off
|
||||
return JwtClaimsSet.builder()
|
||||
.issuer(issuer)
|
||||
.subject("subject")
|
||||
@@ -37,7 +40,8 @@ public class TestJwtClaimsSets {
|
||||
.issuedAt(issuedAt)
|
||||
.notBefore(issuedAt)
|
||||
.expiresAt(expiresAt)
|
||||
.id(UUID.randomUUID().toString())
|
||||
.id("jti")
|
||||
.claim("custom-claim-name", "custom-claim-value");
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcScopes;
|
||||
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
|
||||
import org.springframework.security.oauth2.jose.JoseHeaderNames;
|
||||
import org.springframework.security.oauth2.jwt.JoseHeaderNames;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.jose.JoseHeaderNames;
|
||||
import org.springframework.security.oauth2.jwt.JoseHeaderNames;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +25,7 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken2;
|
||||
import org.springframework.security.oauth2.jose.JoseHeaderNames;
|
||||
import org.springframework.security.oauth2.jwt.JoseHeaderNames;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtEncoder;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
* Copyright 2020-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,62 +15,61 @@
|
||||
*/
|
||||
package org.springframework.security.oauth2.server.authorization.web;
|
||||
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.jwk.ECKey;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.KeyUse;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.crypto.key.AsymmetricKey;
|
||||
import org.springframework.security.crypto.key.CryptoKeySource;
|
||||
import org.springframework.security.crypto.key.SymmetricKey;
|
||||
import org.springframework.security.crypto.key.TestCryptoKeys;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.nimbusds.jose.jwk.ECKey;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.KeyUse;
|
||||
import com.nimbusds.jose.jwk.OctetSequenceKey;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jose.jwk.source.JWKSource;
|
||||
import com.nimbusds.jose.proc.SecurityContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.oauth2.jose.TestJwks;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link JwkSetEndpointFilter}.
|
||||
* Tests for {@link NimbusJwkSetEndpointFilter}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
*/
|
||||
public class JwkSetEndpointFilterTests {
|
||||
private CryptoKeySource keySource;
|
||||
private JwkSetEndpointFilter filter;
|
||||
public class NimbusJwkSetEndpointFilterTests {
|
||||
private JWKSource<SecurityContext> jwkSource;
|
||||
private NimbusJwkSetEndpointFilter filter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.keySource = mock(CryptoKeySource.class);
|
||||
this.filter = new JwkSetEndpointFilter(this.keySource);
|
||||
this.jwkSource = mock(JWKSource.class);
|
||||
this.filter = new NimbusJwkSetEndpointFilter(this.jwkSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenKeySourceNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new JwkSetEndpointFilter(null))
|
||||
public void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new NimbusJwkSetEndpointFilter(null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("keySource cannot be null");
|
||||
.hasMessage("jwkSource cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenJwkSetEndpointUriNullThenThrowIllegalArgumentException() {
|
||||
assertThatThrownBy(() -> new JwkSetEndpointFilter(this.keySource, null))
|
||||
assertThatThrownBy(() -> new NimbusJwkSetEndpointFilter(this.jwkSource, null))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.hasMessage("jwkSetEndpointUri cannot be empty");
|
||||
}
|
||||
@@ -90,7 +89,7 @@ public class JwkSetEndpointFilterTests {
|
||||
|
||||
@Test
|
||||
public void doFilterWhenJwkSetRequestPostThenNotProcessed() throws Exception {
|
||||
String requestUri = JwkSetEndpointFilter.DEFAULT_JWK_SET_ENDPOINT_URI;
|
||||
String requestUri = NimbusJwkSetEndpointFilter.DEFAULT_JWK_SET_ENDPOINT_URI;
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
|
||||
request.setServletPath(requestUri);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -103,12 +102,11 @@ public class JwkSetEndpointFilterTests {
|
||||
|
||||
@Test
|
||||
public void doFilterWhenAsymmetricKeysThenJwkSetResponse() throws Exception {
|
||||
AsymmetricKey rsaKey = TestCryptoKeys.rsaKey().build();
|
||||
AsymmetricKey ecKey = TestCryptoKeys.ecKey().build();
|
||||
when(this.keySource.getKeys()).thenReturn(
|
||||
Stream.of(rsaKey, ecKey).collect(Collectors.toSet()));
|
||||
RSAKey rsaJwk = TestJwks.DEFAULT_RSA_JWK;
|
||||
ECKey ecJwk = TestJwks.DEFAULT_EC_JWK;
|
||||
given(this.jwkSource.get(any(), any())).willReturn(Arrays.asList(rsaJwk, ecJwk));
|
||||
|
||||
String requestUri = JwkSetEndpointFilter.DEFAULT_JWK_SET_ENDPOINT_URI;
|
||||
String requestUri = NimbusJwkSetEndpointFilter.DEFAULT_JWK_SET_ENDPOINT_URI;
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
|
||||
request.setServletPath(requestUri);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
@@ -123,29 +121,25 @@ public class JwkSetEndpointFilterTests {
|
||||
JWKSet jwkSet = JWKSet.parse(response.getContentAsString());
|
||||
assertThat(jwkSet.getKeys()).hasSize(2);
|
||||
|
||||
RSAKey rsaJwk = (RSAKey) jwkSet.getKeyByKeyId(rsaKey.getId());
|
||||
assertThat(rsaJwk).isNotNull();
|
||||
assertThat(rsaJwk.toRSAPublicKey()).isEqualTo(rsaKey.getPublicKey());
|
||||
assertThat(rsaJwk.toRSAPrivateKey()).isNull();
|
||||
assertThat(rsaJwk.getKeyUse()).isEqualTo(KeyUse.SIGNATURE);
|
||||
assertThat(rsaJwk.getAlgorithm()).isEqualTo(JWSAlgorithm.RS256);
|
||||
RSAKey rsaJwkResult = (RSAKey) jwkSet.getKeyByKeyId(rsaJwk.getKeyID());
|
||||
assertThat(rsaJwkResult).isNotNull();
|
||||
assertThat(rsaJwkResult.toRSAPublicKey()).isEqualTo(rsaJwk.toRSAPublicKey());
|
||||
assertThat(rsaJwkResult.toRSAPrivateKey()).isNull();
|
||||
assertThat(rsaJwkResult.getKeyUse()).isEqualTo(KeyUse.SIGNATURE);
|
||||
|
||||
ECKey ecJwk = (ECKey) jwkSet.getKeyByKeyId(ecKey.getId());
|
||||
assertThat(ecJwk).isNotNull();
|
||||
assertThat(ecJwk.toECPublicKey()).isEqualTo(ecKey.getPublicKey());
|
||||
assertThat(ecJwk.toECPublicKey()).isEqualTo(ecKey.getPublicKey());
|
||||
assertThat(ecJwk.toECPrivateKey()).isNull();
|
||||
assertThat(ecJwk.getKeyUse()).isEqualTo(KeyUse.SIGNATURE);
|
||||
assertThat(ecJwk.getAlgorithm()).isEqualTo(JWSAlgorithm.ES256);
|
||||
ECKey ecJwkResult = (ECKey) jwkSet.getKeyByKeyId(ecJwk.getKeyID());
|
||||
assertThat(ecJwkResult).isNotNull();
|
||||
assertThat(ecJwkResult.toECPublicKey()).isEqualTo(ecJwk.toECPublicKey());
|
||||
assertThat(ecJwkResult.toECPrivateKey()).isNull();
|
||||
assertThat(ecJwkResult.getKeyUse()).isEqualTo(KeyUse.SIGNATURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doFilterWhenSymmetricKeysThenJwkSetResponseEmpty() throws Exception {
|
||||
SymmetricKey secretKey = TestCryptoKeys.secretKey().build();
|
||||
when(this.keySource.getKeys()).thenReturn(
|
||||
new HashSet<>(Collections.singleton(secretKey)));
|
||||
OctetSequenceKey secretJwk = TestJwks.DEFAULT_SECRET_JWK;
|
||||
given(this.jwkSource.get(any(), any())).willReturn(Collections.singletonList(secretJwk));
|
||||
|
||||
String requestUri = JwkSetEndpointFilter.DEFAULT_JWK_SET_ENDPOINT_URI;
|
||||
String requestUri = NimbusJwkSetEndpointFilter.DEFAULT_JWK_SET_ENDPOINT_URI;
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
|
||||
request.setServletPath(requestUri);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
Reference in New Issue
Block a user