Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
3c0e7ccb
Commit
3c0e7ccb
authored
Dec 05, 2019
by
Andy Wilkinson
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '2.1.x' into 2.2.x
Closes gh-19304
parents
758b80a0
3e2b466c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
181 additions
and
2 deletions
+181
-2
SslServerCustomizer.java
...ramework/boot/web/embedded/netty/SslServerCustomizer.java
+136
-2
NettyReactiveWebServerFactoryTests.java
...eb/embedded/netty/NettyReactiveWebServerFactoryTests.java
+45
-0
No files found.
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/SslServerCustomizer.java
View file @
3c0e7ccb
...
@@ -16,12 +16,27 @@
...
@@ -16,12 +16,27 @@
package
org
.
springframework
.
boot
.
web
.
embedded
.
netty
;
package
org
.
springframework
.
boot
.
web
.
embedded
.
netty
;
import
java.net.Socket
;
import
java.net.URL
;
import
java.net.URL
;
import
java.security.InvalidAlgorithmParameterException
;
import
java.security.KeyStore
;
import
java.security.KeyStore
;
import
java.security.KeyStoreException
;
import
java.security.NoSuchAlgorithmException
;
import
java.security.Principal
;
import
java.security.PrivateKey
;
import
java.security.Provider
;
import
java.security.UnrecoverableKeyException
;
import
java.security.cert.X509Certificate
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.stream.Collectors
;
import
javax.net.ssl.KeyManager
;
import
javax.net.ssl.KeyManagerFactory
;
import
javax.net.ssl.KeyManagerFactory
;
import
javax.net.ssl.KeyManagerFactorySpi
;
import
javax.net.ssl.ManagerFactoryParameters
;
import
javax.net.ssl.SSLEngine
;
import
javax.net.ssl.TrustManagerFactory
;
import
javax.net.ssl.TrustManagerFactory
;
import
javax.net.ssl.X509ExtendedKeyManager
;
import
io.netty.handler.ssl.ClientAuth
;
import
io.netty.handler.ssl.ClientAuth
;
import
io.netty.handler.ssl.SslContextBuilder
;
import
io.netty.handler.ssl.SslContextBuilder
;
...
@@ -40,6 +55,7 @@ import org.springframework.util.ResourceUtils;
...
@@ -40,6 +55,7 @@ import org.springframework.util.ResourceUtils;
*
*
* @author Brian Clozel
* @author Brian Clozel
* @author Raheela Aslam
* @author Raheela Aslam
* @author Chris Bono
* @since 2.0.0
* @since 2.0.0
*/
*/
public
class
SslServerCustomizer
implements
NettyServerCustomizer
{
public
class
SslServerCustomizer
implements
NettyServerCustomizer
{
...
@@ -92,8 +108,10 @@ public class SslServerCustomizer implements NettyServerCustomizer {
...
@@ -92,8 +108,10 @@ public class SslServerCustomizer implements NettyServerCustomizer {
protected
KeyManagerFactory
getKeyManagerFactory
(
Ssl
ssl
,
SslStoreProvider
sslStoreProvider
)
{
protected
KeyManagerFactory
getKeyManagerFactory
(
Ssl
ssl
,
SslStoreProvider
sslStoreProvider
)
{
try
{
try
{
KeyStore
keyStore
=
getKeyStore
(
ssl
,
sslStoreProvider
);
KeyStore
keyStore
=
getKeyStore
(
ssl
,
sslStoreProvider
);
KeyManagerFactory
keyManagerFactory
=
KeyManagerFactory
KeyManagerFactory
keyManagerFactory
=
(
ssl
.
getKeyAlias
()
==
null
)
.
getInstance
(
KeyManagerFactory
.
getDefaultAlgorithm
());
?
KeyManagerFactory
.
getInstance
(
KeyManagerFactory
.
getDefaultAlgorithm
())
:
ConfigurableAliasKeyManagerFactory
.
instance
(
ssl
.
getKeyAlias
(),
KeyManagerFactory
.
getDefaultAlgorithm
());
char
[]
keyPassword
=
(
ssl
.
getKeyPassword
()
!=
null
)
?
ssl
.
getKeyPassword
().
toCharArray
()
:
null
;
char
[]
keyPassword
=
(
ssl
.
getKeyPassword
()
!=
null
)
?
ssl
.
getKeyPassword
().
toCharArray
()
:
null
;
if
(
keyPassword
==
null
&&
ssl
.
getKeyStorePassword
()
!=
null
)
{
if
(
keyPassword
==
null
&&
ssl
.
getKeyStorePassword
()
!=
null
)
{
keyPassword
=
ssl
.
getKeyStorePassword
().
toCharArray
();
keyPassword
=
ssl
.
getKeyStorePassword
().
toCharArray
();
...
@@ -161,4 +179,120 @@ public class SslServerCustomizer implements NettyServerCustomizer {
...
@@ -161,4 +179,120 @@ public class SslServerCustomizer implements NettyServerCustomizer {
}
}
/**
* A {@link KeyManagerFactory} that allows a configurable key alias to be used. Due to
* the fact that the actual calls to retrieve the key by alias are done at request
* time the approach is to wrap the actual key managers with a
* {@link ConfigurableAliasKeyManager}. The actual SPI has to be wrapped as well due
* to the fact that {@link KeyManagerFactory#getKeyManagers()} is final.
*/
private
static
final
class
ConfigurableAliasKeyManagerFactory
extends
KeyManagerFactory
{
private
static
ConfigurableAliasKeyManagerFactory
instance
(
String
alias
,
String
algorithm
)
throws
NoSuchAlgorithmException
{
KeyManagerFactory
originalFactory
=
KeyManagerFactory
.
getInstance
(
algorithm
);
ConfigurableAliasKeyManagerFactorySpi
spi
=
new
ConfigurableAliasKeyManagerFactorySpi
(
originalFactory
,
alias
);
return
new
ConfigurableAliasKeyManagerFactory
(
spi
,
originalFactory
.
getProvider
(),
algorithm
);
}
private
ConfigurableAliasKeyManagerFactory
(
ConfigurableAliasKeyManagerFactorySpi
spi
,
Provider
provider
,
String
algorithm
)
{
super
(
spi
,
provider
,
algorithm
);
}
}
private
static
final
class
ConfigurableAliasKeyManagerFactorySpi
extends
KeyManagerFactorySpi
{
private
KeyManagerFactory
originalFactory
;
private
String
alias
;
private
ConfigurableAliasKeyManagerFactorySpi
(
KeyManagerFactory
originalFactory
,
String
alias
)
{
this
.
originalFactory
=
originalFactory
;
this
.
alias
=
alias
;
}
@Override
protected
void
engineInit
(
KeyStore
keyStore
,
char
[]
chars
)
throws
KeyStoreException
,
NoSuchAlgorithmException
,
UnrecoverableKeyException
{
this
.
originalFactory
.
init
(
keyStore
,
chars
);
}
@Override
protected
void
engineInit
(
ManagerFactoryParameters
managerFactoryParameters
)
throws
InvalidAlgorithmParameterException
{
throw
new
InvalidAlgorithmParameterException
(
"Unsupported ManagerFactoryParameters"
);
}
@Override
protected
KeyManager
[]
engineGetKeyManagers
()
{
return
Arrays
.
stream
(
this
.
originalFactory
.
getKeyManagers
()).
filter
(
X509ExtendedKeyManager
.
class
::
isInstance
)
.
map
(
X509ExtendedKeyManager
.
class
::
cast
).
map
(
this
::
wrapKeyManager
).
collect
(
Collectors
.
toList
())
.
toArray
(
new
KeyManager
[
0
]);
}
private
ConfigurableAliasKeyManager
wrapKeyManager
(
X509ExtendedKeyManager
km
)
{
return
new
ConfigurableAliasKeyManager
(
km
,
this
.
alias
);
}
}
private
static
final
class
ConfigurableAliasKeyManager
extends
X509ExtendedKeyManager
{
private
final
X509ExtendedKeyManager
keyManager
;
private
final
String
alias
;
private
ConfigurableAliasKeyManager
(
X509ExtendedKeyManager
keyManager
,
String
alias
)
{
this
.
keyManager
=
keyManager
;
this
.
alias
=
alias
;
}
@Override
public
String
chooseEngineClientAlias
(
String
[]
strings
,
Principal
[]
principals
,
SSLEngine
sslEngine
)
{
return
this
.
keyManager
.
chooseEngineClientAlias
(
strings
,
principals
,
sslEngine
);
}
@Override
public
String
chooseEngineServerAlias
(
String
s
,
Principal
[]
principals
,
SSLEngine
sslEngine
)
{
if
(
this
.
alias
==
null
)
{
return
this
.
keyManager
.
chooseEngineServerAlias
(
s
,
principals
,
sslEngine
);
}
return
this
.
alias
;
}
@Override
public
String
chooseClientAlias
(
String
[]
keyType
,
Principal
[]
issuers
,
Socket
socket
)
{
return
this
.
keyManager
.
chooseClientAlias
(
keyType
,
issuers
,
socket
);
}
@Override
public
String
chooseServerAlias
(
String
keyType
,
Principal
[]
issuers
,
Socket
socket
)
{
return
this
.
keyManager
.
chooseServerAlias
(
keyType
,
issuers
,
socket
);
}
@Override
public
X509Certificate
[]
getCertificateChain
(
String
alias
)
{
return
this
.
keyManager
.
getCertificateChain
(
alias
);
}
@Override
public
String
[]
getClientAliases
(
String
keyType
,
Principal
[]
issuers
)
{
return
this
.
keyManager
.
getClientAliases
(
keyType
,
issuers
);
}
@Override
public
PrivateKey
getPrivateKey
(
String
alias
)
{
return
this
.
keyManager
.
getPrivateKey
(
alias
);
}
@Override
public
String
[]
getServerAliases
(
String
keyType
,
Principal
[]
issuers
)
{
return
this
.
keyManager
.
getServerAliases
(
keyType
,
issuers
);
}
}
}
}
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java
View file @
3c0e7ccb
...
@@ -16,15 +16,25 @@
...
@@ -16,15 +16,25 @@
package
org
.
springframework
.
boot
.
web
.
embedded
.
netty
;
package
org
.
springframework
.
boot
.
web
.
embedded
.
netty
;
import
java.time.Duration
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
javax.net.ssl.SSLHandshakeException
;
import
org.junit.jupiter.api.Test
;
import
org.junit.jupiter.api.Test
;
import
org.mockito.InOrder
;
import
org.mockito.InOrder
;
import
reactor.core.publisher.Mono
;
import
reactor.netty.http.server.HttpServer
;
import
reactor.netty.http.server.HttpServer
;
import
reactor.test.StepVerifier
;
import
org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory
;
import
org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory
;
import
org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests
;
import
org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests
;
import
org.springframework.boot.web.server.PortInUseException
;
import
org.springframework.boot.web.server.PortInUseException
;
import
org.springframework.boot.web.server.Ssl
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.client.reactive.ReactorClientHttpConnector
;
import
org.springframework.web.reactive.function.BodyInserters
;
import
org.springframework.web.reactive.function.client.WebClient
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThatExceptionOfType
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThatExceptionOfType
;
...
@@ -37,6 +47,7 @@ import static org.mockito.Mockito.mock;
...
@@ -37,6 +47,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link NettyReactiveWebServerFactory}.
* Tests for {@link NettyReactiveWebServerFactory}.
*
*
* @author Brian Clozel
* @author Brian Clozel
* @author Chris Bono
*/
*/
class
NettyReactiveWebServerFactoryTests
extends
AbstractReactiveWebServerFactoryTests
{
class
NettyReactiveWebServerFactoryTests
extends
AbstractReactiveWebServerFactoryTests
{
...
@@ -83,4 +94,38 @@ class NettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor
...
@@ -83,4 +94,38 @@ class NettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor
assertForwardHeaderIsUsed
(
factory
);
assertForwardHeaderIsUsed
(
factory
);
}
}
@Test
void
whenSslIsConfiguredWithAValidAliasARequestSucceeds
()
{
Mono
<
String
>
result
=
testSslWithAlias
(
"test-alias"
);
StepVerifier
.
setDefaultTimeout
(
Duration
.
ofSeconds
(
30
));
StepVerifier
.
create
(
result
).
expectNext
(
"Hello World"
).
verifyComplete
();
}
@Test
void
whenSslIsConfiguredWithAnInvalidAliasTheSslHandshakeFails
()
{
Mono
<
String
>
result
=
testSslWithAlias
(
"test-alias-bad"
);
StepVerifier
.
setDefaultTimeout
(
Duration
.
ofSeconds
(
30
));
StepVerifier
.
create
(
result
).
expectErrorMatches
((
throwable
)
->
throwable
instanceof
SSLHandshakeException
&&
throwable
.
getMessage
().
contains
(
"HANDSHAKE_FAILURE"
)).
verify
();
}
protected
Mono
<
String
>
testSslWithAlias
(
String
alias
)
{
String
keyStore
=
"classpath:test.jks"
;
String
keyPassword
=
"password"
;
NettyReactiveWebServerFactory
factory
=
getFactory
();
Ssl
ssl
=
new
Ssl
();
ssl
.
setKeyStore
(
keyStore
);
ssl
.
setKeyPassword
(
keyPassword
);
ssl
.
setKeyAlias
(
alias
);
factory
.
setSsl
(
ssl
);
this
.
webServer
=
factory
.
getWebServer
(
new
EchoHandler
());
this
.
webServer
.
start
();
ReactorClientHttpConnector
connector
=
buildTrustAllSslConnector
();
WebClient
client
=
WebClient
.
builder
().
baseUrl
(
"https://localhost:"
+
this
.
webServer
.
getPort
())
.
clientConnector
(
connector
).
build
();
return
client
.
post
().
uri
(
"/test"
).
contentType
(
MediaType
.
TEXT_PLAIN
)
.
body
(
BodyInserters
.
fromObject
(
"Hello World"
)).
exchange
()
.
flatMap
((
response
)
->
response
.
bodyToMono
(
String
.
class
));
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment