-
Notifications
You must be signed in to change notification settings - Fork 6k
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
Conversation
There was a problem hiding this 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;
crypto/src/main/java/org/springframework/security/crypto/factory/RsaKeyConverters.java
Outdated
Show resolved
Hide resolved
@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. |
As discussed offline I will give you some time to polish this (i.e. move addConverters to be private within the post processor) |
@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 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. |
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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("-----") ? |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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?
@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)); |
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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?
722ad3f
to
e5ba1fd
Compare
There was a problem hiding this 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
crypto/spring-security-crypto.gradle
Outdated
@@ -1,6 +1,7 @@ | |||
apply plugin: 'io.spring.convention.spring-module' | |||
|
|||
dependencies { | |||
optional 'org.springframework:spring-core' |
There was a problem hiding this comment.
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.
@rwinch I moved Note that I placed these in |
This is now merged into |
Fixes: gh-6494