Skip to content

Commit 1e072d4

Browse files
committed
In-progress review comments.
1 parent d5f3968 commit 1e072d4

File tree

6 files changed

+114
-62
lines changed

6 files changed

+114
-62
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package io.grpc.internal;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.security.GeneralSecurityException;
6+
import java.security.KeyStore;
7+
import java.security.cert.Certificate;
8+
import java.security.cert.CertificateException;
9+
import java.security.cert.CertificateFactory;
10+
import java.security.cert.X509Certificate;
11+
import java.util.Arrays;
12+
import java.util.Collection;
13+
import java.util.Optional;
14+
import javax.net.ssl.TrustManager;
15+
import javax.net.ssl.TrustManagerFactory;
16+
import javax.net.ssl.X509ExtendedTrustManager;
17+
import javax.security.auth.x500.X500Principal;
18+
19+
public class CertificateUtils {
20+
/**
21+
* Creates a X509ExtendedTrustManager using the provided CA certs if applicable for the
22+
* certificate type.
23+
*/
24+
public static Optional<TrustManager> getX509ExtendedTrustManager(InputStream rootCerts)
25+
throws GeneralSecurityException {
26+
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
27+
try {
28+
ks.load(null, null);
29+
} catch (IOException ex) {
30+
// Shouldn't really happen, as we're not loading any data.
31+
throw new GeneralSecurityException(ex);
32+
}
33+
X509Certificate[] certs = CertificateUtils.getX509Certificates(rootCerts);
34+
for (X509Certificate cert : certs) {
35+
X500Principal principal = cert.getSubjectX500Principal();
36+
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
37+
}
38+
39+
TrustManagerFactory trustManagerFactory =
40+
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
41+
trustManagerFactory.init(ks);
42+
return Arrays.stream(trustManagerFactory.getTrustManagers())
43+
.filter(trustManager -> trustManager instanceof X509ExtendedTrustManager).findFirst();
44+
}
45+
46+
private static X509Certificate[] getX509Certificates(InputStream inputStream)
47+
throws CertificateException {
48+
CertificateFactory factory = CertificateFactory.getInstance("X.509");
49+
Collection<? extends Certificate> certs = factory.generateCertificates(inputStream);
50+
return certs.toArray(new X509Certificate[0]);
51+
}
52+
}

examples/example-tls/build.gradle

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ def protocVersion = '3.25.5'
3030
dependencies {
3131
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
3232
implementation "io.grpc:grpc-stub:${grpcVersion}"
33-
implementation "io.grpc:grpc-api:${grpcVersion}"
3433
compileOnly "org.apache.tomcat:annotations-api:6.0.53"
3534
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
3635
}
@@ -75,8 +74,6 @@ application {
7574
applicationDistribution.into('bin') {
7675
from(helloWorldTlsServer)
7776
from(helloWorldTlsClient)
78-
filePermissions {
79-
unix(0755)
80-
}
77+
fileMode = 0755
8178
}
8279
}

netty/src/main/java/io/grpc/netty/NettyClientTransport.java

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -203,32 +203,25 @@ public ClientStream newStream(
203203
if (channel == null) {
204204
return new FailingClientStream(statusExplainingWhyTheChannelIsNull, tracers);
205205
}
206-
if (negotiator instanceof ClientTlsProtocolNegotiator && callOptions.getAuthority() != null) {
207-
ClientTlsProtocolNegotiator clientTlsProtocolNegotiator =
208-
(ClientTlsProtocolNegotiator) negotiator;
209-
if (!clientTlsProtocolNegotiator.canVerifyAuthorityOverride()) {
210-
return new FailingClientStream(Status.INTERNAL.withDescription(
211-
"Can't allow authority override in rpc when X509ExtendedTrustManager is not available"),
212-
tracers);
213-
}
214-
boolean peerVerified;
215-
if (authoritiesAllowedForPeer.containsKey(callOptions.getAuthority())) {
216-
peerVerified = authoritiesAllowedForPeer.get(callOptions.getAuthority());
217-
} else {
218-
try {
219-
clientTlsProtocolNegotiator.verifyAuthorityAllowedForPeerCert(callOptions.getAuthority());
220-
peerVerified = true;
221-
} catch (SSLPeerUnverifiedException | CertificateException e) {
222-
peerVerified = false;
223-
logger.log(Level.FINE, "Peer hostname verification failed for authority '{}'.",
224-
callOptions.getAuthority());
225-
}
226-
authoritiesAllowedForPeer.put(callOptions.getAuthority(), peerVerified);
227-
}
228-
if (!peerVerified) {
206+
try {
207+
if (callOptions.getAuthority() != null && !negotiator.mayBeVerifyAuthority(
208+
callOptions.getAuthority())) {
209+
logger.log(Level.FINE, "Peer hostname verification failed for authority '{}'.",
210+
callOptions.getAuthority());
229211
return new FailingClientStream(Status.INTERNAL.withDescription(
230212
"Peer hostname verification failed for authority"), tracers);
231213
}
214+
} catch (UnsupportedOperationException ex) {
215+
logger.log(Level.FINE, "Can't allow authority override in rpc when X509ExtendedTrustManager is not available.",
216+
callOptions.getAuthority());
217+
return new FailingClientStream(Status.INTERNAL.withDescription(
218+
"Can't allow authority override in rpc when X509ExtendedTrustManager is not available"),
219+
tracers);
220+
} catch (SSLPeerUnverifiedException | CertificateException ex) {
221+
logger.log(Level.FINE, "Peer hostname verification failed for authority '{}'.",
222+
callOptions.getAuthority());
223+
return new FailingClientStream(Status.INTERNAL.withDescription(
224+
"Peer hostname verification failed for authority"), tracers);
232225
}
233226
StatsTraceContext statsTraceCtx =
234227
StatsTraceContext.newClientContext(tracers, getAttributes(), headers);

netty/src/main/java/io/grpc/netty/ProtocolNegotiator.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import io.grpc.internal.ObjectPool;
2020
import io.netty.channel.ChannelHandler;
2121
import io.netty.util.AsciiString;
22+
import java.security.cert.CertificateException;
2223
import java.util.concurrent.Executor;
24+
import javax.net.ssl.SSLPeerUnverifiedException;
2325

2426
/**
2527
* An class that provides a Netty handler to control protocol negotiation.
@@ -63,4 +65,15 @@ interface ServerFactory {
6365
*/
6466
ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool);
6567
}
68+
69+
/**
70+
* Verify the authority against peer if applicable depending on the transport credential type.
71+
* @throws UnsupportedOperationException if the verification should happen but the required
72+
* type of TrustManager could not be found.
73+
* @throws SSLPeerUnverifiedException if peer verification failed
74+
* @throws CertificateException if certificates have a problem
75+
*/
76+
default boolean mayBeVerifyAuthority(String authority) throws SSLPeerUnverifiedException, CertificateException {
77+
return true;
78+
}
6679
}

netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import static com.google.common.base.Preconditions.checkNotNull;
2020
import static com.google.common.base.Preconditions.checkState;
21-
import static io.grpc.util.CertificateUtils.getX509ExtendedTrustManager;
21+
import static io.grpc.internal.CertificateUtils.getX509ExtendedTrustManager;
2222

2323
import com.google.common.annotations.VisibleForTesting;
2424
import com.google.common.base.Preconditions;
@@ -42,6 +42,7 @@
4242
import io.grpc.Status;
4343
import io.grpc.TlsChannelCredentials;
4444
import io.grpc.TlsServerCredentials;
45+
import io.grpc.internal.FailingClientStream;
4546
import io.grpc.internal.GrpcAttributes;
4647
import io.grpc.internal.GrpcUtil;
4748
import io.grpc.internal.ObjectPool;
@@ -85,6 +86,7 @@
8586
import java.util.concurrent.Executor;
8687
import java.util.logging.Level;
8788
import java.util.logging.Logger;
89+
import javax.annotation.Nonnull;
8890
import javax.annotation.Nullable;
8991
import javax.net.ssl.SSLEngine;
9092
import javax.net.ssl.SSLEngineResult;
@@ -640,6 +642,35 @@ public void setSslEngine(SSLEngine sslEngine) {
640642
boolean hasX509ExtendedTrustManager() {
641643
return x509ExtendedTrustManager != null;
642644
}
645+
646+
public boolean mayBeVerifyAuthority(@Nonnull String authority) {
647+
ClientTlsProtocolNegotiator clientTlsProtocolNegotiator =
648+
(ClientTlsProtocolNegotiator) negotiator;
649+
if (!clientTlsProtocolNegotiator.canVerifyAuthorityOverride()) {
650+
return new FailingClientStream(Status.INTERNAL.withDescription(
651+
"Can't allow authority override in rpc when X509ExtendedTrustManager is not available"),
652+
tracers);
653+
}
654+
boolean peerVerified;
655+
if (authoritiesAllowedForPeer.containsKey(callOptions.getAuthority())) {
656+
peerVerified = authoritiesAllowedForPeer.get(callOptions.getAuthority());
657+
} else {
658+
try {
659+
clientTlsProtocolNegotiator.verifyAuthorityAllowedForPeerCert(
660+
callOptions.getAuthority());
661+
peerVerified = true;
662+
} catch (SSLPeerUnverifiedException | CertificateException e) {
663+
peerVerified = false;
664+
logger.log(Level.FINE, "Peer hostname verification failed for authority '{}'.",
665+
callOptions.getAuthority());
666+
}
667+
authoritiesAllowedForPeer.put(callOptions.getAuthority(), peerVerified);
668+
}
669+
if (!peerVerified) {
670+
return new FailingClientStream(Status.INTERNAL.withDescription(
671+
"Peer hostname verification failed for authority"), tracers);
672+
}
673+
}
643674
}
644675

645676
static final class ClientTlsHandler extends ProtocolNegotiationHandler {

util/src/main/java/io/grpc/util/CertificateUtils.java

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
import java.io.InputStream;
2424
import java.io.InputStreamReader;
2525
import java.io.UnsupportedEncodingException;
26-
import java.security.GeneralSecurityException;
2726
import java.security.KeyFactory;
28-
import java.security.KeyStore;
2927
import java.security.NoSuchAlgorithmException;
3028
import java.security.PrivateKey;
3129
import java.security.cert.Certificate;
@@ -34,13 +32,7 @@
3432
import java.security.cert.X509Certificate;
3533
import java.security.spec.InvalidKeySpecException;
3634
import java.security.spec.PKCS8EncodedKeySpec;
37-
import java.util.Arrays;
3835
import java.util.Collection;
39-
import java.util.Optional;
40-
import javax.net.ssl.TrustManager;
41-
import javax.net.ssl.TrustManagerFactory;
42-
import javax.net.ssl.X509ExtendedTrustManager;
43-
import javax.security.auth.x500.X500Principal;
4436

4537
/**
4638
* Contains certificate/key PEM file utility method(s).
@@ -99,31 +91,5 @@ public static PrivateKey getPrivateKey(InputStream inputStream)
9991
}
10092
}
10193
}
102-
103-
/**
104-
* Creates a X509ExtendedTrustManager using the provided CA certs if applicable for the
105-
* certificate type.
106-
*/
107-
public static Optional<TrustManager> getX509ExtendedTrustManager(InputStream rootCerts)
108-
throws GeneralSecurityException {
109-
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
110-
try {
111-
ks.load(null, null);
112-
} catch (IOException ex) {
113-
// Shouldn't really happen, as we're not loading any data.
114-
throw new GeneralSecurityException(ex);
115-
}
116-
X509Certificate[] certs = CertificateUtils.getX509Certificates(rootCerts);
117-
for (X509Certificate cert : certs) {
118-
X500Principal principal = cert.getSubjectX500Principal();
119-
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
120-
}
121-
122-
TrustManagerFactory trustManagerFactory =
123-
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
124-
trustManagerFactory.init(ks);
125-
return Arrays.stream(trustManagerFactory.getTrustManagers())
126-
.filter(trustManager -> trustManager instanceof X509ExtendedTrustManager).findFirst();
127-
}
12894
}
12995

0 commit comments

Comments
 (0)