Skip to content

Introduce Support for Reading RSA Keys #6505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 14, 2019
Merged

Conversation

jzheaux
Copy link
Contributor

@jzheaux jzheaux commented Feb 4, 2019

Fixes: gh-6494

Copy link
Member

@rwinch rwinch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have provided feedback inline.

Also, we might consider how this is integrated with Spring's ApplicationContext. Should we provide support to ensure that it is automatically included? Should we also support conversion from a Resource to a key? For example:

@Value("classpath:key.private")
RSAPrivateKey key;

@jzheaux
Copy link
Contributor Author

jzheaux commented Feb 7, 2019

@rwinch, I've added to more commits to this PR which address integrating with the application context.

I'm partial to actually having that in a separate PR, but if you feel like the changes are largely obvious/non-controversial, then I'm fine reviewing it all here.

@rwinch
Copy link
Member

rwinch commented Feb 7, 2019

As discussed offline I will give you some time to polish this (i.e. move addConverters to be private within the post processor)

@jzheaux
Copy link
Contributor Author

jzheaux commented Feb 7, 2019

@rwinch, I've applied some polish. First, I moved the converter registration into the post-processor. Also, the post-processor has been simplified a bit, to implement BeanFactoryPostProcessor instead of BeanDefinitionRegistryPostProcessor.

Second, I consolidated and simplified the ApplicationContext-integration tests.

I think this looks much better now, and I'm okay with having everything reviewed together.

Copy link
Member

@rwinch rwinch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates! I have provided feedback/questions inline

* @author Josh Cummings
* @since 5.2
*/
public class ConversionServicePostProcessor implements BeanFactoryPostProcessor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there is a reason this is so generic given it adds RSA converts. I also wonder if it makes sense in the current package since RSA is applicable to non web apps and to reactive applications.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick search across the code base shows a handful of classes that extend BeanFactoryPostProcessor. For example, there is one for ldap, one for debug, and one for websocket, among others.

Each of these is in spring-security-config and follows the pattern org.springframework.security.config.{module}.

So, it seems natural to rename this as org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor since it is post processing the ConversionService to support RSA keys.


private Converter<String, InputStream> pemInputStreamConverter() {
ResourceLoader resourceLoader = new DefaultResourceLoader();
return source -> source.startsWith("-----") ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help me understand what the conditional logic is for? It looks like this might be to handle things like classpath:foo.pem. However, my concern is that it should not interfere with existing String to InputStream conversions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work if a variable (i.e. ${some.path} is used)? I cannot recall if anything special is necessary, but it has bitten me before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding your first question, the actual registered converter is String -> RSAPublicKey so it will only be invoked when the target type is RSAPublicKey. Yes, the conditional logic is to differentiate between a raw key and one referred to via classpath:foo.pem.

I didn't test your second case, but I will double-check this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the sample to use ${some.property.name} to confirm that works.

}

private Converter<String, InputStream> pemInputStreamConverter() {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make the ResourceLoader a member variable and allow it to be injected.

*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ConversionService service = beanFactory.getConversionService();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Spring always register a ConversionService (XML, Java Config, and Boot)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, XML and Java Config do not register a ConversionService. If the user (or Boot) does not supply one then Spring will fall back to the PropertyEditor conversion style.

I didn't look into property editors - would you recommend that in order to provide more in-depth support?

@jzheaux
Copy link
Contributor Author

jzheaux commented Mar 2, 2019

@rwinch, I've responded to your comments. Let me know what you think.

public static Converter<InputStream, RSAPrivateKey> pkcs8() {
KeyFactory keyFactory = rsaFactory();
return source -> {
BufferedReader reader = new BufferedReader(new InputStreamReader(source));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I'm missing something, but shouldn't we close the InputStream after reading it? If not, we should make it clear we don't close the InputStream in the Javadoc and explain why.

public static Converter<InputStream, RSAPublicKey> x509() {
KeyFactory keyFactory = rsaFactory();
return source -> {
BufferedReader reader = new BufferedReader(new InputStreamReader(source));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I'm missing something, but shouldn't we close the InputStream after reading it? If not, we should make it clear we don't close the InputStream in the Javadoc and explain why.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. My intent was to not make an assumption about the nature of the stream being passed in. So, I'll add a Javadoc to both methods.

That said, I think it makes sense to close it in the post processor since it's the post processor that opens it, so I'll change that, too.

BufferedReader reader = new BufferedReader(new InputStreamReader(source));
List<String> lines = reader.lines().collect(Collectors.toList());
Assert.isTrue(!lines.isEmpty() && lines.get(0).startsWith(X509_PEM_HEADER),
"Key is not in PEM-encoded X.509 format");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add details about the X509_PEM_HEADER is missing to the error message?

BufferedReader reader = new BufferedReader(new InputStreamReader(source));
List<String> lines = reader.lines().collect(Collectors.toList());
Assert.isTrue(!lines.isEmpty() && lines.get(0).startsWith(PKCS8_PEM_HEADER),
"Key is not in PEM-encoded PKCS#8 format");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add details about PKCS8_PEM_HEADER missing to the error message?

@jzheaux jzheaux force-pushed the gh-6494 branch 5 times, most recently from 722ad3f to e5ba1fd Compare March 19, 2019 19:37
Copy link
Member

@rwinch rwinch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I provided feedback inline

@@ -1,6 +1,7 @@
apply plugin: 'io.spring.convention.spring-module'

dependencies {
optional 'org.springframework:spring-core'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for not realizing this till late in the game...I'm realizing we have a tangle here. The core includes crypto and now we have optional dependency on core. I think we need to move the crypo classes that use core into core.

@jzheaux
Copy link
Contributor Author

jzheaux commented Mar 29, 2019

@rwinch I moved RsaKeyConverters and RsaKeyConvertersTest over to core. Otherwise, I simply cleaned up the commits a bit so now we just have two: One for the new feature and one for the change in one of the resource server samples.

Note that I placed these in org.springframework.security.converter, which is a new package. I chose it because converter is the tail of the package name for Converter itself, which aligns nicely, I think. Let me know, though, if there is a better spot for it.

@jzheaux jzheaux merged commit b252371 into spring-projects:master Apr 14, 2019
@jzheaux jzheaux deleted the gh-6494 branch April 14, 2019 01:39
@jzheaux
Copy link
Contributor Author

jzheaux commented Apr 14, 2019

This is now merged into master. (And I'm super excited about this feature!)

@jzheaux jzheaux self-assigned this Apr 14, 2019
@jzheaux jzheaux added this to the 5.2.0.M2 milestone Apr 14, 2019
@jzheaux jzheaux added in: core An issue in spring-security-core status: duplicate A duplicate of another issue New Feature labels Apr 14, 2019
@rwinch rwinch added the type: enhancement A general enhancement label May 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core An issue in spring-security-core status: duplicate A duplicate of another issue type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add RSA Key Converters
2 participants