mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-01 09:42:13 +00:00
Introduce Support for Reading RSA Keys
Fixes: gh-6494
This commit is contained in:
parent
22c8f63390
commit
1c25fe26c9
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -18,12 +18,12 @@ package org.springframework.security.config.annotation.web.configuration;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -40,6 +40,7 @@ import org.springframework.security.config.annotation.ObjectPostProcessor;
|
|||||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
|
||||||
import org.springframework.security.context.DelegatingApplicationListener;
|
import org.springframework.security.context.DelegatingApplicationListener;
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
import org.springframework.security.web.FilterInvocation;
|
||||||
@ -159,6 +160,11 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
|
|||||||
this.webSecurityConfigurers = webSecurityConfigurers;
|
this.webSecurityConfigurers = webSecurityConfigurers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public static BeanFactoryPostProcessor conversionServicePostProcessor() {
|
||||||
|
return new RsaKeyConversionServicePostProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
|
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
|
||||||
ConfigurableListableBeanFactory beanFactory) {
|
ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -20,10 +20,12 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
|
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
@ -66,6 +68,11 @@ class WebFluxSecurityConfiguration {
|
|||||||
return new CsrfRequestDataValueProcessor();
|
return new CsrfRequestDataValueProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public static BeanFactoryPostProcessor conversionServicePostProcessor() {
|
||||||
|
return new RsaKeyConversionServicePostProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
private List<SecurityWebFilterChain> getSecurityWebFilterChains() {
|
private List<SecurityWebFilterChain> getSecurityWebFilterChains() {
|
||||||
List<SecurityWebFilterChain> result = this.securityWebFilterChains;
|
List<SecurityWebFilterChain> result = this.securityWebFilterChains;
|
||||||
if (ObjectUtils.isEmpty(result)) {
|
if (ObjectUtils.isEmpty(result)) {
|
||||||
|
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.config.crypto;
|
||||||
|
|
||||||
|
import java.beans.PropertyEditorSupport;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||||
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
import org.springframework.security.converter.RsaKeyConverters;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds {@link RsaKeyConverters} to the configured {@link ConversionService} or {@link PropertyEditor}s
|
||||||
|
*
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public class RsaKeyConversionServicePostProcessor implements BeanFactoryPostProcessor {
|
||||||
|
private static final String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
|
||||||
|
|
||||||
|
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
|
|
||||||
|
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||||
|
Assert.notNull(resourceLoader, "resourceLoader cannot be null");
|
||||||
|
this.resourceLoader = resourceLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||||
|
if (hasUserDefinedConversionService(beanFactory)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Converter<String, RSAPrivateKey> pkcs8 = pkcs8();
|
||||||
|
Converter<String, RSAPublicKey> x509 = x509();
|
||||||
|
|
||||||
|
ConversionService service = beanFactory.getConversionService();
|
||||||
|
if (service instanceof ConverterRegistry) {
|
||||||
|
ConverterRegistry registry = (ConverterRegistry) service;
|
||||||
|
registry.addConverter(String.class, RSAPrivateKey.class, pkcs8);
|
||||||
|
registry.addConverter(String.class, RSAPublicKey.class, x509);
|
||||||
|
} else {
|
||||||
|
beanFactory.addPropertyEditorRegistrar(registry -> {
|
||||||
|
registry.registerCustomEditor(RSAPublicKey.class, new ConverterPropertyEditorAdapter<>(x509));
|
||||||
|
registry.registerCustomEditor(RSAPrivateKey.class, new ConverterPropertyEditorAdapter<>(pkcs8));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasUserDefinedConversionService(ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
return beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
|
||||||
|
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Converter<String, RSAPrivateKey> pkcs8() {
|
||||||
|
Converter<String, InputStream> pemInputStreamConverter = pemInputStreamConverter();
|
||||||
|
Converter<InputStream, RSAPrivateKey> pkcs8KeyConverter = autoclose(RsaKeyConverters.pkcs8());
|
||||||
|
return pair(pemInputStreamConverter, pkcs8KeyConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Converter<String, RSAPublicKey> x509() {
|
||||||
|
Converter<String, InputStream> pemInputStreamConverter = pemInputStreamConverter();
|
||||||
|
Converter<InputStream, RSAPublicKey> x509KeyConverter = autoclose(RsaKeyConverters.x509());
|
||||||
|
return pair(pemInputStreamConverter, x509KeyConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Converter<String, InputStream> pemInputStreamConverter() {
|
||||||
|
return source -> source.startsWith("-----") ?
|
||||||
|
toInputStream(source) : toInputStream(this.resourceLoader.getResource(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream toInputStream(String raw) {
|
||||||
|
return new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream toInputStream(Resource resource) {
|
||||||
|
try {
|
||||||
|
return resource.getInputStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> Converter<InputStream, T> autoclose(Converter<InputStream, T> inputStreamKeyConverter) {
|
||||||
|
return inputStream -> {
|
||||||
|
try (InputStream is = inputStream) {
|
||||||
|
return inputStreamKeyConverter.convert(is);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private <S, T, I> Converter<S, T> pair(Converter<S, I> one, Converter<I, T> two) {
|
||||||
|
return source -> {
|
||||||
|
I intermediary = one.convert(source);
|
||||||
|
return two.convert(intermediary);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ConverterPropertyEditorAdapter<T> extends PropertyEditorSupport {
|
||||||
|
private final Converter<String, T> converter;
|
||||||
|
|
||||||
|
public ConverterPropertyEditorAdapter(Converter<String, T> converter) {
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAsText() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAsText(String text) throws IllegalArgumentException {
|
||||||
|
if (StringUtils.hasText(text)) {
|
||||||
|
setValue(this.converter.convert(text));
|
||||||
|
} else {
|
||||||
|
setValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.config.crypto;
|
||||||
|
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.support.GenericConversionService;
|
||||||
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.test.SpringTestRule;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RsaKeyConversionServicePostProcessor}
|
||||||
|
*/
|
||||||
|
public class RsaKeyConversionServicePostProcessorTests {
|
||||||
|
|
||||||
|
private static final String PKCS8_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
|
||||||
|
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMk7CKSTfu3QoV\n" +
|
||||||
|
"HoPVXxwZO+qweztd36cVWYqGOZinrOR2crWFu50AgR2CsdIH0+cqo7F4Vx7/3O8i\n" +
|
||||||
|
"RpYYZPe2VoO5sumzJt8P6fS80/TAKjhJDAqgZKRJTgGN8KxCM6p/aJli1ZeDBqiV\n" +
|
||||||
|
"v7vJJe+ZgJuPGRS+HMNa/wPxEkqqXsglcJcQV1ZEtfKXSHB7jizKpRL38185SyAC\n" +
|
||||||
|
"pwyjvBu6Cmm1URfhQo88mf239ONh4dZ2HoDfzN1q6Ssu4F4hgutxr9B0DVLDP5u+\n" +
|
||||||
|
"WFrm3nsJ76zf99uJ+ntMUHJ+bY+gOjSlVWIVBIZeAaEGKCNWRk/knjvjbijpvm3U\n" +
|
||||||
|
"acGlgdL3AgMBAAECggEACxxxS7zVyu91qI2s5eSKmAQAXMqgup6+2hUluc47nqUv\n" +
|
||||||
|
"uZz/c/6MPkn2Ryo+65d4IgqmMFjSfm68B/2ER5FTcvoLl1Xo2twrrVpUmcg3BClS\n" +
|
||||||
|
"IZPuExdhVNnxjYKEWwcyZrehyAoR261fDdcFxLRW588efIUC+rPTTRHzAc7sT+Ln\n" +
|
||||||
|
"t/uFeYNWJm3LaegOLoOmlMAhJ5puAWSN1F0FxtRf/RVgzbLA9QC975SKHJsfWCSr\n" +
|
||||||
|
"IZyPsdeaqomKaF65l8nfqlE0Ua2L35gIOGKjUwb7uUE8nI362RWMtYdoi3zDDyoY\n" +
|
||||||
|
"hSFbgjylCHDM0u6iSh6KfqOHtkYyJ8tUYgVWl787wQKBgQDYO3wL7xuDdD101Lyl\n" +
|
||||||
|
"AnaDdFB9fxp83FG1cWr+t7LYm9YxGfEUsKHAJXN6TIayDkOOoVwIl+Gz0T3Z06Bm\n" +
|
||||||
|
"eBGLrB9mrVA7+C7NJwu5gTMlzP6HxUR9zKJIQ/VB1NUGM77LSmvOFbHc9Q0+z8EH\n" +
|
||||||
|
"X5WO516a3Z7lNtZJcCoPOtu2rwKBgQCmbj41Fh+SSEUApCEKms5ETRpe7LXQlJgx\n" +
|
||||||
|
"yW7zcJNNuIb1C3vBLPxjiOTMgYKOeMg5rtHTGLT43URHLh9ArjawasjSAr4AM3J4\n" +
|
||||||
|
"xpoi/sKGDdiKOsuDWIGfzdYL8qyTHSdpZLQsCTMRiRYgAHZFPgNa7SLZRfZicGlr\n" +
|
||||||
|
"GHN1rJW6OQKBgEjiM/upyrJSWeypUDSmUeAZMpA6aWkwsfHgmtnkfUn5rQa74cDB\n" +
|
||||||
|
"kKO9e+D7LmOR3z+SL/1NhGwh2SE07dncGr3jdGodfO/ZxZyszozmeaECKcEFwwJM\n" +
|
||||||
|
"GV8WWPKplGwUwPiwywmZ0mvRxXcoe73KgBS88+xrSwWjqDL0tZiQlEJNAoGATkei\n" +
|
||||||
|
"GMQMG3jEg9Wu+NbxV6zQT3+U0MNjhl9RQU1c63x0dcNt9OFc4NAdlZcAulRTENaK\n" +
|
||||||
|
"OHjxffBM0hH+fySx8m53gFfr2BpaqDX5f6ZGBlly1SlsWZ4CchCVsc71nshipi7I\n" +
|
||||||
|
"k8HL9F5/OpQdDNprJ5RMBNfkWE65Nrcsb1e6oPkCgYAxwgdiSOtNg8PjDVDmAhwT\n" +
|
||||||
|
"Mxj0Dtwi2fAqQ76RVrrXpNp3uCOIAu4CfruIb5llcJ3uak0ZbnWri32AxSgk80y3\n" +
|
||||||
|
"EWiRX/WEDu5znejF+5O3pI02atWWcnxifEKGGlxwkcMbQdA67MlrJLFaSnnGpNXo\n" +
|
||||||
|
"yPfcul058SOqhafIZQMEKQ==\n" +
|
||||||
|
"-----END PRIVATE KEY-----";
|
||||||
|
private static final String X509_PUBLIC_KEY_LOCATION =
|
||||||
|
"classpath:org/springframework/security/config/annotation/web/configuration/simple.pub";
|
||||||
|
|
||||||
|
private final RsaKeyConversionServicePostProcessor postProcessor =
|
||||||
|
new RsaKeyConversionServicePostProcessor();
|
||||||
|
private ConversionService service;
|
||||||
|
|
||||||
|
@Value("classpath:org/springframework/security/config/annotation/web/configuration/simple.pub")
|
||||||
|
RSAPublicKey publicKey;
|
||||||
|
|
||||||
|
@Value("classpath:org/springframework/security/config/annotation/web/configuration/simple.priv")
|
||||||
|
RSAPrivateKey privateKey;
|
||||||
|
|
||||||
|
@Value("custom:simple.pub")
|
||||||
|
RSAPublicKey samePublicKey;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final SpringTestRule spring = new SpringTestRule();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
ConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||||
|
beanFactory.setConversionService(new GenericConversionService());
|
||||||
|
this.postProcessor.postProcessBeanFactory(beanFactory);
|
||||||
|
this.service = beanFactory.getConversionService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenUsingConversionServiceForRawKeyThenOk() {
|
||||||
|
RSAPrivateKey key = this.service.convert(PKCS8_PRIVATE_KEY, RSAPrivateKey.class);
|
||||||
|
assertThat(key.getModulus().bitLength()).isEqualTo(2048);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenUsingConversionServiceForClasspathThenOk() {
|
||||||
|
RSAPublicKey key = this.service.convert(X509_PUBLIC_KEY_LOCATION, RSAPublicKey.class);
|
||||||
|
assertThat(key.getModulus().bitLength()).isEqualTo(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void valueWhenReferringToClasspathPublicKeyThenConverts() {
|
||||||
|
this.spring.register(CustomResourceLoaderConfig.class, DefaultConfig.class).autowire();
|
||||||
|
assertThat(this.publicKey.getModulus().bitLength()).isEqualTo(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void valueWhenReferringToClasspathPrivateKeyThenConverts() {
|
||||||
|
this.spring.register(CustomResourceLoaderConfig.class, DefaultConfig.class).autowire();
|
||||||
|
assertThat(this.privateKey.getModulus().bitLength()).isEqualTo(2048);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void valueWhenReferringToCustomResourceLoadedPublicKeyThenConverts() {
|
||||||
|
this.spring.register(CustomResourceLoaderConfig.class, DefaultConfig.class).autowire();
|
||||||
|
assertThat(this.samePublicKey.getModulus().bitLength()).isEqualTo(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void valueWhenOverridingConversionServiceThenUsed() {
|
||||||
|
assertThatCode(() ->
|
||||||
|
this.spring.register(OverrideConversionServiceConfig.class, DefaultConfig.class).autowire())
|
||||||
|
.hasRootCauseInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class DefaultConfig { }
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class CustomResourceLoaderConfig {
|
||||||
|
@Bean
|
||||||
|
BeanFactoryPostProcessor conversionServiceCustomizer() {
|
||||||
|
return beanFactory -> beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)
|
||||||
|
.setResourceLoader(new CustomResourceLoader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class OverrideConversionServiceConfig {
|
||||||
|
@Bean
|
||||||
|
ConversionService conversionService() {
|
||||||
|
GenericConversionService service = new GenericConversionService();
|
||||||
|
service.addConverter(String.class, RSAPublicKey.class, source -> {
|
||||||
|
throw new IllegalArgumentException("unsupported");
|
||||||
|
});
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CustomResourceLoader implements ResourceLoader {
|
||||||
|
private final ResourceLoader delegate = new DefaultResourceLoader();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Resource getResource(String location) {
|
||||||
|
if (location.startsWith("classpath:")) {
|
||||||
|
return this.delegate.getResource(location);
|
||||||
|
} else if (location.startsWith("custom:")) {
|
||||||
|
String[] parts = location.split(":");
|
||||||
|
return this.delegate.getResource(
|
||||||
|
"classpath:org/springframework/security/config/annotation/web/configuration/" + parts[1]);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("unsupported resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader getClassLoader() {
|
||||||
|
return this.delegate.getClassLoader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,7 @@ import reactor.core.publisher.Mono;
|
|||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
@ -174,6 +175,16 @@ public class OAuth2ResourceServerSpecTests {
|
|||||||
.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith("Bearer error=\"invalid_token\""));
|
.expectHeader().value(HttpHeaders.WWW_AUTHENTICATE, startsWith("Bearer error=\"invalid_token\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenValidUsingPlaceholderThenReturnsOk() {
|
||||||
|
this.spring.register(PlaceholderConfig.class, RootController.class).autowire();
|
||||||
|
|
||||||
|
this.client.get()
|
||||||
|
.headers(headers -> headers.setBearerAuth(this.messageReadToken))
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWhenCustomDecoderThenAuthenticatesAccordingly() {
|
public void getWhenCustomDecoderThenAuthenticatesAccordingly() {
|
||||||
this.spring.register(CustomDecoderConfig.class, RootController.class).autowire();
|
this.spring.register(CustomDecoderConfig.class, RootController.class).autowire();
|
||||||
@ -383,6 +394,29 @@ public class OAuth2ResourceServerSpecTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableWebFlux
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
static class PlaceholderConfig {
|
||||||
|
@Value("${classpath:org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests-simple.pub}")
|
||||||
|
RSAPublicKey key;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.authorizeExchange()
|
||||||
|
.anyExchange().hasAuthority("SCOPE_message:read")
|
||||||
|
.and()
|
||||||
|
.oauth2ResourceServer()
|
||||||
|
.jwt()
|
||||||
|
.publicKey(this.key);
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EnableWebFlux
|
@EnableWebFlux
|
||||||
@EnableWebFluxSecurity
|
@EnableWebFluxSecurity
|
||||||
static class JwkSetUriConfig {
|
static class JwkSetUriConfig {
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMk7CKSTfu3QoV
|
||||||
|
HoPVXxwZO+qweztd36cVWYqGOZinrOR2crWFu50AgR2CsdIH0+cqo7F4Vx7/3O8i
|
||||||
|
RpYYZPe2VoO5sumzJt8P6fS80/TAKjhJDAqgZKRJTgGN8KxCM6p/aJli1ZeDBqiV
|
||||||
|
v7vJJe+ZgJuPGRS+HMNa/wPxEkqqXsglcJcQV1ZEtfKXSHB7jizKpRL38185SyAC
|
||||||
|
pwyjvBu6Cmm1URfhQo88mf239ONh4dZ2HoDfzN1q6Ssu4F4hgutxr9B0DVLDP5u+
|
||||||
|
WFrm3nsJ76zf99uJ+ntMUHJ+bY+gOjSlVWIVBIZeAaEGKCNWRk/knjvjbijpvm3U
|
||||||
|
acGlgdL3AgMBAAECggEACxxxS7zVyu91qI2s5eSKmAQAXMqgup6+2hUluc47nqUv
|
||||||
|
uZz/c/6MPkn2Ryo+65d4IgqmMFjSfm68B/2ER5FTcvoLl1Xo2twrrVpUmcg3BClS
|
||||||
|
IZPuExdhVNnxjYKEWwcyZrehyAoR261fDdcFxLRW588efIUC+rPTTRHzAc7sT+Ln
|
||||||
|
t/uFeYNWJm3LaegOLoOmlMAhJ5puAWSN1F0FxtRf/RVgzbLA9QC975SKHJsfWCSr
|
||||||
|
IZyPsdeaqomKaF65l8nfqlE0Ua2L35gIOGKjUwb7uUE8nI362RWMtYdoi3zDDyoY
|
||||||
|
hSFbgjylCHDM0u6iSh6KfqOHtkYyJ8tUYgVWl787wQKBgQDYO3wL7xuDdD101Lyl
|
||||||
|
AnaDdFB9fxp83FG1cWr+t7LYm9YxGfEUsKHAJXN6TIayDkOOoVwIl+Gz0T3Z06Bm
|
||||||
|
eBGLrB9mrVA7+C7NJwu5gTMlzP6HxUR9zKJIQ/VB1NUGM77LSmvOFbHc9Q0+z8EH
|
||||||
|
X5WO516a3Z7lNtZJcCoPOtu2rwKBgQCmbj41Fh+SSEUApCEKms5ETRpe7LXQlJgx
|
||||||
|
yW7zcJNNuIb1C3vBLPxjiOTMgYKOeMg5rtHTGLT43URHLh9ArjawasjSAr4AM3J4
|
||||||
|
xpoi/sKGDdiKOsuDWIGfzdYL8qyTHSdpZLQsCTMRiRYgAHZFPgNa7SLZRfZicGlr
|
||||||
|
GHN1rJW6OQKBgEjiM/upyrJSWeypUDSmUeAZMpA6aWkwsfHgmtnkfUn5rQa74cDB
|
||||||
|
kKO9e+D7LmOR3z+SL/1NhGwh2SE07dncGr3jdGodfO/ZxZyszozmeaECKcEFwwJM
|
||||||
|
GV8WWPKplGwUwPiwywmZ0mvRxXcoe73KgBS88+xrSwWjqDL0tZiQlEJNAoGATkei
|
||||||
|
GMQMG3jEg9Wu+NbxV6zQT3+U0MNjhl9RQU1c63x0dcNt9OFc4NAdlZcAulRTENaK
|
||||||
|
OHjxffBM0hH+fySx8m53gFfr2BpaqDX5f6ZGBlly1SlsWZ4CchCVsc71nshipi7I
|
||||||
|
k8HL9F5/OpQdDNprJ5RMBNfkWE65Nrcsb1e6oPkCgYAxwgdiSOtNg8PjDVDmAhwT
|
||||||
|
Mxj0Dtwi2fAqQ76RVrrXpNp3uCOIAu4CfruIb5llcJ3uak0ZbnWri32AxSgk80y3
|
||||||
|
EWiRX/WEDu5znejF+5O3pI02atWWcnxifEKGGlxwkcMbQdA67MlrJLFaSnnGpNXo
|
||||||
|
yPfcul058SOqhafIZQMEKQ==
|
||||||
|
-----END PRIVATE KEY-----
|
@ -0,0 +1,7 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
|
||||||
|
UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
|
||||||
|
HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D
|
||||||
|
o2kQ+X5xK9cipRgEKwIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
|
@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0IUjrPZDz+3z0UE4ppcKU36v7hnh8FJjhu3lbJYj0qj9eZiwEJxi9HHUfSK1DhUQG7mJBbYTK1tPYCgre5EkfKh+64VhYUa+vz17zYCmuB8fFj4XHE3MLkWIG+AUn8hNbPzYYmiBTjfGnMKxLHjsbdTiF4mtn+85w366916R6midnAuiPD4HjZaZ1PAsuY60gr8bhMEDtJ8unz81hoQrozpBZJ6r8aR1PrsWb1OqPMloK9kAIutJNvWYKacp8WYAp2WWy72PxQ7Fb0eIA1br3A5dnp+Cln6JROJcZUIRJ+QvS6QONWeS2407uQmS+i+lybsqaH0ldYC7NBEBA5inPQIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.converter;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for creating {@link java.security.Key} converter instances
|
||||||
|
*
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public class RsaKeyConverters {
|
||||||
|
private static final String DASHES = "-----";
|
||||||
|
private static final String PKCS8_PEM_HEADER = DASHES + "BEGIN PRIVATE KEY" + DASHES;
|
||||||
|
private static final String PKCS8_PEM_FOOTER = DASHES + "END PRIVATE KEY" + DASHES;
|
||||||
|
private static final String X509_PEM_HEADER = DASHES + "BEGIN PUBLIC KEY" + DASHES;
|
||||||
|
private static final String X509_PEM_FOOTER = DASHES + "END PUBLIC KEY" + DASHES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link Converter} for converting a PEM-encoded PKCS#8 RSA Private Key
|
||||||
|
* into a {@link RSAPrivateKey}.
|
||||||
|
*
|
||||||
|
* Note that keys are often formatted in PKCS#1 and this can easily be identified by the header.
|
||||||
|
* If the key file begins with "-----BEGIN RSA PRIVATE KEY-----", then it is PKCS#1. If it is
|
||||||
|
* PKCS#8 formatted, then it begins with "-----BEGIN PRIVATE KEY-----".
|
||||||
|
*
|
||||||
|
* This converter does not close the {@link InputStream} in order to avoid making non-portable
|
||||||
|
* assumptions about the streams' origin and further use.
|
||||||
|
*
|
||||||
|
* @return A {@link Converter} that can read a PEM-encoded PKCS#8 RSA Private Key and return a
|
||||||
|
* {@link RSAPrivateKey}.
|
||||||
|
*/
|
||||||
|
public static Converter<InputStream, RSAPrivateKey> pkcs8() {
|
||||||
|
KeyFactory keyFactory = rsaFactory();
|
||||||
|
return source -> {
|
||||||
|
List<String> lines = readAllLines(source);
|
||||||
|
Assert.isTrue(!lines.isEmpty() && lines.get(0).startsWith(PKCS8_PEM_HEADER),
|
||||||
|
"Key is not in PEM-encoded PKCS#8 format, " +
|
||||||
|
"please check that the header begins with -----" + PKCS8_PEM_HEADER + "-----");
|
||||||
|
String base64Encoded = lines.stream()
|
||||||
|
.filter(RsaKeyConverters::isNotPkcs8Wrapper)
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
byte[] pkcs8 = Base64.getDecoder().decode(base64Encoded);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (RSAPrivateKey) keyFactory.generatePrivate(
|
||||||
|
new PKCS8EncodedKeySpec(pkcs8));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link Converter} for converting a PEM-encoded X.509 RSA Public Key
|
||||||
|
* into a {@link RSAPublicKey}.
|
||||||
|
*
|
||||||
|
* This converter does not close the {@link InputStream} in order to avoid making non-portable
|
||||||
|
* assumptions about the streams' origin and further use.
|
||||||
|
*
|
||||||
|
* @return A {@link Converter} that can read a PEM-encoded X.509 RSA Public Key and return a
|
||||||
|
* {@link RSAPublicKey}.
|
||||||
|
*/
|
||||||
|
public static Converter<InputStream, RSAPublicKey> x509() {
|
||||||
|
KeyFactory keyFactory = rsaFactory();
|
||||||
|
return source -> {
|
||||||
|
List<String> lines = readAllLines(source);
|
||||||
|
Assert.isTrue(!lines.isEmpty() && lines.get(0).startsWith(X509_PEM_HEADER),
|
||||||
|
"Key is not in PEM-encoded X.509 format, " +
|
||||||
|
"please check that the header begins with -----" + X509_PEM_HEADER + "-----");
|
||||||
|
String base64Encoded = lines.stream()
|
||||||
|
.filter(RsaKeyConverters::isNotX509Wrapper)
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
byte[] x509 = Base64.getDecoder().decode(base64Encoded);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (RSAPublicKey) keyFactory.generatePublic(
|
||||||
|
new X509EncodedKeySpec(x509));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> readAllLines(InputStream source) {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(source));
|
||||||
|
return reader.lines().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KeyFactory rsaFactory() {
|
||||||
|
try {
|
||||||
|
return KeyFactory.getInstance("RSA");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNotPkcs8Wrapper(String line) {
|
||||||
|
return !PKCS8_PEM_HEADER.equals(line) && !PKCS8_PEM_FOOTER.equals(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNotX509Wrapper(String line) {
|
||||||
|
return !X509_PEM_HEADER.equals(line) && !X509_PEM_FOOTER.equals(line);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.converter;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.interfaces.RSAPrivateCrtKey;
|
||||||
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.assertj.core.api.AssertionsForClassTypes;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RsaKeyConverters}
|
||||||
|
*/
|
||||||
|
public class RsaKeyConvertersTest {
|
||||||
|
private static final String PKCS8_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
|
||||||
|
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCMk7CKSTfu3QoV\n" +
|
||||||
|
"HoPVXxwZO+qweztd36cVWYqGOZinrOR2crWFu50AgR2CsdIH0+cqo7F4Vx7/3O8i\n" +
|
||||||
|
"RpYYZPe2VoO5sumzJt8P6fS80/TAKjhJDAqgZKRJTgGN8KxCM6p/aJli1ZeDBqiV\n" +
|
||||||
|
"v7vJJe+ZgJuPGRS+HMNa/wPxEkqqXsglcJcQV1ZEtfKXSHB7jizKpRL38185SyAC\n" +
|
||||||
|
"pwyjvBu6Cmm1URfhQo88mf239ONh4dZ2HoDfzN1q6Ssu4F4hgutxr9B0DVLDP5u+\n" +
|
||||||
|
"WFrm3nsJ76zf99uJ+ntMUHJ+bY+gOjSlVWIVBIZeAaEGKCNWRk/knjvjbijpvm3U\n" +
|
||||||
|
"acGlgdL3AgMBAAECggEACxxxS7zVyu91qI2s5eSKmAQAXMqgup6+2hUluc47nqUv\n" +
|
||||||
|
"uZz/c/6MPkn2Ryo+65d4IgqmMFjSfm68B/2ER5FTcvoLl1Xo2twrrVpUmcg3BClS\n" +
|
||||||
|
"IZPuExdhVNnxjYKEWwcyZrehyAoR261fDdcFxLRW588efIUC+rPTTRHzAc7sT+Ln\n" +
|
||||||
|
"t/uFeYNWJm3LaegOLoOmlMAhJ5puAWSN1F0FxtRf/RVgzbLA9QC975SKHJsfWCSr\n" +
|
||||||
|
"IZyPsdeaqomKaF65l8nfqlE0Ua2L35gIOGKjUwb7uUE8nI362RWMtYdoi3zDDyoY\n" +
|
||||||
|
"hSFbgjylCHDM0u6iSh6KfqOHtkYyJ8tUYgVWl787wQKBgQDYO3wL7xuDdD101Lyl\n" +
|
||||||
|
"AnaDdFB9fxp83FG1cWr+t7LYm9YxGfEUsKHAJXN6TIayDkOOoVwIl+Gz0T3Z06Bm\n" +
|
||||||
|
"eBGLrB9mrVA7+C7NJwu5gTMlzP6HxUR9zKJIQ/VB1NUGM77LSmvOFbHc9Q0+z8EH\n" +
|
||||||
|
"X5WO516a3Z7lNtZJcCoPOtu2rwKBgQCmbj41Fh+SSEUApCEKms5ETRpe7LXQlJgx\n" +
|
||||||
|
"yW7zcJNNuIb1C3vBLPxjiOTMgYKOeMg5rtHTGLT43URHLh9ArjawasjSAr4AM3J4\n" +
|
||||||
|
"xpoi/sKGDdiKOsuDWIGfzdYL8qyTHSdpZLQsCTMRiRYgAHZFPgNa7SLZRfZicGlr\n" +
|
||||||
|
"GHN1rJW6OQKBgEjiM/upyrJSWeypUDSmUeAZMpA6aWkwsfHgmtnkfUn5rQa74cDB\n" +
|
||||||
|
"kKO9e+D7LmOR3z+SL/1NhGwh2SE07dncGr3jdGodfO/ZxZyszozmeaECKcEFwwJM\n" +
|
||||||
|
"GV8WWPKplGwUwPiwywmZ0mvRxXcoe73KgBS88+xrSwWjqDL0tZiQlEJNAoGATkei\n" +
|
||||||
|
"GMQMG3jEg9Wu+NbxV6zQT3+U0MNjhl9RQU1c63x0dcNt9OFc4NAdlZcAulRTENaK\n" +
|
||||||
|
"OHjxffBM0hH+fySx8m53gFfr2BpaqDX5f6ZGBlly1SlsWZ4CchCVsc71nshipi7I\n" +
|
||||||
|
"k8HL9F5/OpQdDNprJ5RMBNfkWE65Nrcsb1e6oPkCgYAxwgdiSOtNg8PjDVDmAhwT\n" +
|
||||||
|
"Mxj0Dtwi2fAqQ76RVrrXpNp3uCOIAu4CfruIb5llcJ3uak0ZbnWri32AxSgk80y3\n" +
|
||||||
|
"EWiRX/WEDu5znejF+5O3pI02atWWcnxifEKGGlxwkcMbQdA67MlrJLFaSnnGpNXo\n" +
|
||||||
|
"yPfcul058SOqhafIZQMEKQ==\n" +
|
||||||
|
"-----END PRIVATE KEY-----";
|
||||||
|
|
||||||
|
private static final String PKCS1_PRIVATE_KEY =
|
||||||
|
"-----BEGIN RSA PRIVATE KEY-----\n" +
|
||||||
|
"MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw\n" +
|
||||||
|
"33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW\n" +
|
||||||
|
"+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB\n" +
|
||||||
|
"AoGAD+onAtVye4ic7VR7V50DF9bOnwRwNXrARcDhq9LWNRrRGElESYYTQ6EbatXS\n" +
|
||||||
|
"3MCyjjX2eMhu/aF5YhXBwkppwxg+EOmXeh+MzL7Zh284OuPbkglAaGhV9bb6/5Cp\n" +
|
||||||
|
"uGb1esyPbYW+Ty2PC0GSZfIXkXs76jXAu9TOBvD0ybc2YlkCQQDywg2R/7t3Q2OE\n" +
|
||||||
|
"2+yo382CLJdrlSLVROWKwb4tb2PjhY4XAwV8d1vy0RenxTB+K5Mu57uVSTHtrMK0\n" +
|
||||||
|
"GAtFr833AkEA6avx20OHo61Yela/4k5kQDtjEf1N0LfI+BcWZtxsS3jDM3i1Hp0K\n" +
|
||||||
|
"Su5rsCPb8acJo5RO26gGVrfAsDcIXKC+bQJAZZ2XIpsitLyPpuiMOvBbzPavd4gY\n" +
|
||||||
|
"6Z8KWrfYzJoI/Q9FuBo6rKwl4BFoToD7WIUS+hpkagwWiz+6zLoX1dbOZwJACmH5\n" +
|
||||||
|
"fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523\n" +
|
||||||
|
"Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP\n" +
|
||||||
|
"FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==\n" +
|
||||||
|
"-----END RSA PRIVATE KEY-----";
|
||||||
|
|
||||||
|
private static final String X509_PUBLIC_KEY =
|
||||||
|
"-----BEGIN PUBLIC KEY-----\n" +
|
||||||
|
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\n" +
|
||||||
|
"UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\n" +
|
||||||
|
"HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\n" +
|
||||||
|
"o2kQ+X5xK9cipRgEKwIDAQAB\n" +
|
||||||
|
"-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
|
private static final String MALFORMED_X509_KEY = "malformed";
|
||||||
|
|
||||||
|
private final Converter<InputStream, RSAPublicKey> x509 = RsaKeyConverters.x509();
|
||||||
|
private final Converter<InputStream, RSAPrivateKey> pkcs8 = RsaKeyConverters.pkcs8();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pkcs8WhenConvertingPkcs8PrivateKeyThenOk() {
|
||||||
|
RSAPrivateKey key = this.pkcs8.convert(toInputStream(PKCS8_PRIVATE_KEY));
|
||||||
|
Assertions.assertThat(key).isInstanceOf(RSAPrivateCrtKey.class);
|
||||||
|
Assertions.assertThat(key.getModulus().bitLength()).isEqualTo(2048);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pkcs8WhenConvertingPkcs1PrivateKeyThenIllegalArgumentException() {
|
||||||
|
AssertionsForClassTypes.assertThatCode(() -> this.pkcs8.convert(toInputStream(PKCS1_PRIVATE_KEY)))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void x509WhenConverteringX509PublicKeyThenOk() {
|
||||||
|
RSAPublicKey key = this.x509.convert(toInputStream(X509_PUBLIC_KEY));
|
||||||
|
Assertions.assertThat(key.getModulus().bitLength()).isEqualTo(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void x509WhenConvertingDerEncodedX509PublicKeyThenIllegalArgumentException() {
|
||||||
|
AssertionsForClassTypes.assertThatCode(() -> this.x509.convert(toInputStream(MALFORMED_X509_KEY)))
|
||||||
|
.isInstanceOf(IllegalArgumentException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputStream toInputStream(String string) {
|
||||||
|
return new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user