Skip to content

Commit 8e3df86

Browse files
authored
Merge pull request #605 from yidongnan/feature/keystoreSupport
Add keystore support
2 parents 8ce5b13 + ed56965 commit 8e3df86

File tree

15 files changed

+904
-122
lines changed

15 files changed

+904
-122
lines changed

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/NettyChannelFactory.java

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@
2020
import static java.util.Objects.requireNonNull;
2121
import static net.devh.boot.grpc.common.util.GrpcUtils.DOMAIN_SOCKET_ADDRESS_SCHEME;
2222

23-
import java.io.IOException;
2423
import java.io.InputStream;
2524
import java.net.URI;
2625
import java.util.List;
2726

27+
import javax.net.ssl.KeyManagerFactory;
2828
import javax.net.ssl.SSLException;
29+
import javax.net.ssl.TrustManagerFactory;
2930

3031
import org.springframework.core.io.Resource;
3132

@@ -40,6 +41,7 @@
4041
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
4142
import net.devh.boot.grpc.client.config.NegotiationType;
4243
import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
44+
import net.devh.boot.grpc.common.security.KeyStoreUtils;
4345
import net.devh.boot.grpc.common.util.GrpcUtils;
4446

4547
/**
@@ -51,7 +53,6 @@
5153
*
5254
* @author Michael ([email protected])
5355
* @author Daniel Theuke ([email protected])
54-
* @since 5/17/16
5556
*/
5657
// Keep this file in sync with ShadedNettyChannelFactory
5758
public class NettyChannelFactory extends AbstractChannelFactory<NettyChannelBuilder> {
@@ -88,6 +89,7 @@ protected NettyChannelBuilder newChannelBuilder(final String name) {
8889
}
8990

9091
@Override
92+
// Keep this in sync with ShadedNettyChannelFactory#configureSecurity
9193
protected void configureSecurity(final NettyChannelBuilder builder, final String name) {
9294
final GrpcChannelProperties properties = getPropertiesFor(name);
9395

@@ -103,28 +105,8 @@ protected void configureSecurity(final NettyChannelBuilder builder, final String
103105
}
104106

105107
final SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
106-
107-
if (security.isClientAuthEnabled()) {
108-
final Resource certificateChain =
109-
requireNonNull(security.getCertificateChain(), "certificateChain not configured");
110-
final Resource privateKey = requireNonNull(security.getPrivateKey(), "privateKey not configured");
111-
try (InputStream certificateChainStream = certificateChain.getInputStream();
112-
InputStream privateKeyStream = privateKey.getInputStream()) {
113-
sslContextBuilder.keyManager(certificateChainStream, privateKeyStream,
114-
security.getPrivateKeyPassword());
115-
} catch (IOException | RuntimeException e) {
116-
throw new IllegalArgumentException("Failed to create SSLContext (PK/Cert)", e);
117-
}
118-
}
119-
120-
final Resource trustCertCollection = security.getTrustCertCollection();
121-
if (trustCertCollection != null) {
122-
try (InputStream trustCertCollectionStream = trustCertCollection.getInputStream()) {
123-
sslContextBuilder.trustManager(trustCertCollectionStream);
124-
} catch (IOException | RuntimeException e) {
125-
throw new IllegalArgumentException("Failed to create SSLContext (TrustStore)", e);
126-
}
127-
}
108+
configureProvidedClientCertificate(security, sslContextBuilder);
109+
configureAcceptedServerCertificates(security, sslContextBuilder);
128110

129111
if (security.getCiphers() != null && !security.getCiphers().isEmpty()) {
130112
sslContextBuilder.ciphers(security.getCiphers());
@@ -142,12 +124,81 @@ protected void configureSecurity(final NettyChannelBuilder builder, final String
142124
}
143125
}
144126

127+
/**
128+
* Configures the client certificate provided by the ssl context.
129+
*
130+
* @param security The security configuration to use.
131+
* @param sslContextBuilder The ssl context builder to configure.
132+
*/
133+
// Keep this in sync with ShadedNettyChannelFactory#configureProvidedClientCertificate
134+
protected static void configureProvidedClientCertificate(final Security security,
135+
final SslContextBuilder sslContextBuilder) {
136+
if (security.isClientAuthEnabled()) {
137+
try {
138+
final Resource privateKey = security.getPrivateKey();
139+
final Resource keyStore = security.getKeyStore();
140+
141+
if (privateKey != null) {
142+
final Resource certificateChain =
143+
requireNonNull(security.getCertificateChain(), "certificateChain");
144+
final String privateKeyPassword = security.getPrivateKeyPassword();
145+
try (InputStream certificateChainStream = certificateChain.getInputStream();
146+
InputStream privateKeyStream = privateKey.getInputStream()) {
147+
sslContextBuilder.keyManager(certificateChainStream, privateKeyStream, privateKeyPassword);
148+
}
149+
150+
} else if (keyStore != null) {
151+
final KeyManagerFactory keyManagerFactory = KeyStoreUtils.loadKeyManagerFactory(
152+
security.getKeyStoreFormat(), keyStore, security.getKeyStorePassword());
153+
sslContextBuilder.keyManager(keyManagerFactory);
154+
155+
} else {
156+
throw new IllegalStateException("Neither privateKey nor keyStore configured");
157+
}
158+
} catch (final Exception e) {
159+
throw new IllegalArgumentException("Failed to create SSLContext (PK/Cert)", e);
160+
}
161+
}
162+
}
163+
164+
/**
165+
* Configures the server certificates accepted by the ssl context.
166+
*
167+
* @param security The security configuration to use.
168+
* @param sslContextBuilder The ssl context builder to configure.
169+
*/
170+
// Keep this in sync with ShadedNettyChannelFactory#configureAcceptedServerCertificates
171+
protected static void configureAcceptedServerCertificates(final Security security,
172+
final SslContextBuilder sslContextBuilder) {
173+
try {
174+
final Resource trustCertCollection = security.getTrustCertCollection();
175+
final Resource trustStore = security.getTrustStore();
176+
177+
if (trustCertCollection != null) {
178+
try (InputStream trustCertCollectionStream = trustCertCollection.getInputStream()) {
179+
sslContextBuilder.trustManager(trustCertCollectionStream);
180+
}
181+
182+
} else if (trustStore != null) {
183+
final TrustManagerFactory trustManagerFactory = KeyStoreUtils.loadTrustManagerFactory(
184+
security.getTrustStoreFormat(), trustStore, security.getTrustStorePassword());
185+
sslContextBuilder.trustManager(trustManagerFactory);
186+
187+
} else {
188+
// Use system default
189+
}
190+
} catch (final Exception e) {
191+
throw new IllegalArgumentException("Failed to create SSLContext (TrustStore)", e);
192+
}
193+
}
194+
145195
/**
146196
* Converts the given negotiation type to netty's negotiation type.
147197
*
148198
* @param negotiationType The negotiation type to convert.
149199
* @return The converted negotiation type.
150200
*/
201+
// Keep this in sync with ShadedNettyChannelFactory#of
151202
protected static io.grpc.netty.NegotiationType of(final NegotiationType negotiationType) {
152203
switch (negotiationType) {
153204
case PLAINTEXT:

grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/channelfactory/ShadedNettyChannelFactory.java

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@
2020
import static java.util.Objects.requireNonNull;
2121
import static net.devh.boot.grpc.common.util.GrpcUtils.DOMAIN_SOCKET_ADDRESS_SCHEME;
2222

23-
import java.io.IOException;
2423
import java.io.InputStream;
2524
import java.net.URI;
2625
import java.util.List;
2726

27+
import javax.net.ssl.KeyManagerFactory;
2828
import javax.net.ssl.SSLException;
29+
import javax.net.ssl.TrustManagerFactory;
2930

3031
import org.springframework.core.io.Resource;
3132

@@ -40,6 +41,7 @@
4041
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
4142
import net.devh.boot.grpc.client.config.NegotiationType;
4243
import net.devh.boot.grpc.client.interceptor.GlobalClientInterceptorRegistry;
44+
import net.devh.boot.grpc.common.security.KeyStoreUtils;
4345
import net.devh.boot.grpc.common.util.GrpcUtils;
4446

4547
/**
@@ -85,7 +87,9 @@ protected NettyChannelBuilder newChannelBuilder(final String name) {
8587
}
8688
}
8789

90+
8891
@Override
92+
// Keep this in sync with NettyChannelFactory#configureSecurity
8993
protected void configureSecurity(final NettyChannelBuilder builder, final String name) {
9094
final GrpcChannelProperties properties = getPropertiesFor(name);
9195

@@ -101,28 +105,8 @@ protected void configureSecurity(final NettyChannelBuilder builder, final String
101105
}
102106

103107
final SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
104-
105-
if (security.isClientAuthEnabled()) {
106-
final Resource certificateChain =
107-
requireNonNull(security.getCertificateChain(), "certificateChain not configured");
108-
final Resource privateKey = requireNonNull(security.getPrivateKey(), "privateKey not configured");
109-
try (InputStream certificateChainStream = certificateChain.getInputStream();
110-
InputStream privateKeyStream = privateKey.getInputStream()) {
111-
sslContextBuilder.keyManager(certificateChainStream, privateKeyStream,
112-
security.getPrivateKeyPassword());
113-
} catch (IOException | RuntimeException e) {
114-
throw new IllegalArgumentException("Failed to create SSLContext (PK/Cert)", e);
115-
}
116-
}
117-
118-
final Resource trustCertCollection = security.getTrustCertCollection();
119-
if (trustCertCollection != null) {
120-
try (InputStream trustCertCollectionStream = trustCertCollection.getInputStream()) {
121-
sslContextBuilder.trustManager(trustCertCollectionStream);
122-
} catch (IOException | RuntimeException e) {
123-
throw new IllegalArgumentException("Failed to create SSLContext (TrustStore)", e);
124-
}
125-
}
108+
configureProvidedClientCertificate(security, sslContextBuilder);
109+
configureAcceptedServerCertificates(security, sslContextBuilder);
126110

127111
if (security.getCiphers() != null && !security.getCiphers().isEmpty()) {
128112
sslContextBuilder.ciphers(security.getCiphers());
@@ -140,12 +124,81 @@ protected void configureSecurity(final NettyChannelBuilder builder, final String
140124
}
141125
}
142126

127+
/**
128+
* Configures the client certificate provided by the ssl context.
129+
*
130+
* @param security The security configuration to use.
131+
* @param sslContextBuilder The ssl context builder to configure.
132+
*/
133+
// Keep this in sync with NettyChannelFactory#configureProvidedClientCertificate
134+
protected static void configureProvidedClientCertificate(final Security security,
135+
final SslContextBuilder sslContextBuilder) {
136+
if (security.isClientAuthEnabled()) {
137+
try {
138+
final Resource privateKey = security.getPrivateKey();
139+
final Resource keyStore = security.getKeyStore();
140+
141+
if (privateKey != null) {
142+
final Resource certificateChain =
143+
requireNonNull(security.getCertificateChain(), "certificateChain");
144+
final String privateKeyPassword = security.getPrivateKeyPassword();
145+
try (InputStream certificateChainStream = certificateChain.getInputStream();
146+
InputStream privateKeyStream = privateKey.getInputStream()) {
147+
sslContextBuilder.keyManager(certificateChainStream, privateKeyStream, privateKeyPassword);
148+
}
149+
150+
} else if (keyStore != null) {
151+
final KeyManagerFactory keyManagerFactory = KeyStoreUtils.loadKeyManagerFactory(
152+
security.getKeyStoreFormat(), keyStore, security.getKeyStorePassword());
153+
sslContextBuilder.keyManager(keyManagerFactory);
154+
155+
} else {
156+
throw new IllegalStateException("Neither privateKey nor keyStore configured");
157+
}
158+
} catch (final Exception e) {
159+
throw new IllegalArgumentException("Failed to create SSLContext (PK/Cert)", e);
160+
}
161+
}
162+
}
163+
164+
/**
165+
* Configures the server certificates accepted by the ssl context.
166+
*
167+
* @param security The security configuration to use.
168+
* @param sslContextBuilder The ssl context builder to configure.
169+
*/
170+
// Keep this in sync with NettyChannelFactory#configureAcceptedServerCertificates
171+
protected static void configureAcceptedServerCertificates(final Security security,
172+
final SslContextBuilder sslContextBuilder) {
173+
try {
174+
final Resource trustCertCollection = security.getTrustCertCollection();
175+
final Resource trustStore = security.getTrustStore();
176+
177+
if (trustCertCollection != null) {
178+
try (InputStream trustCertCollectionStream = trustCertCollection.getInputStream()) {
179+
sslContextBuilder.trustManager(trustCertCollectionStream);
180+
}
181+
182+
} else if (trustStore != null) {
183+
final TrustManagerFactory trustManagerFactory = KeyStoreUtils.loadTrustManagerFactory(
184+
security.getTrustStoreFormat(), trustStore, security.getTrustStorePassword());
185+
sslContextBuilder.trustManager(trustManagerFactory);
186+
187+
} else {
188+
// Use system default
189+
}
190+
} catch (final Exception e) {
191+
throw new IllegalArgumentException("Failed to create SSLContext (TrustStore)", e);
192+
}
193+
}
194+
143195
/**
144196
* Converts the given negotiation type to netty's negotiation type.
145197
*
146198
* @param negotiationType The negotiation type to convert.
147199
* @return The converted negotiation type.
148200
*/
201+
// Keep this in sync with NettyChannelFactory#of
149202
protected static io.grpc.netty.shaded.io.grpc.netty.NegotiationType of(final NegotiationType negotiationType) {
150203
switch (negotiationType) {
151204
case PLAINTEXT:

0 commit comments

Comments
 (0)