Skip to content

Commit 23a0822

Browse files
committed
in-progress changes.
1 parent 60bfa06 commit 23a0822

File tree

4 files changed

+145
-168
lines changed

4 files changed

+145
-168
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import java.io.ByteArrayInputStream;
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.security.GeneralSecurityException;
23+
import java.security.KeyStore;
24+
import java.security.cert.Certificate;
25+
import java.security.cert.CertificateException;
26+
import java.security.cert.CertificateFactory;
27+
import java.security.cert.X509Certificate;
28+
import java.util.Collection;
29+
import javax.net.ssl.TrustManager;
30+
import javax.net.ssl.TrustManagerFactory;
31+
import javax.security.auth.x500.X500Principal;
32+
33+
/**
34+
* Contains certificate/key PEM file utility method(s) for internal usage.
35+
*/
36+
public class CertificateUtils {
37+
/**
38+
* Creates X509TrustManagers using the provided CA certs.
39+
*/
40+
public static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException {
41+
InputStream rootCertsStream = new ByteArrayInputStream(rootCerts);
42+
try {
43+
return io.grpc.internal.CertificateUtils.createTrustManager(rootCertsStream);
44+
} finally {
45+
GrpcUtil.closeQuietly(rootCertsStream);
46+
}
47+
}
48+
49+
/**
50+
* Creates X509TrustManagers using the provided input stream of CA certs.
51+
*/
52+
public static TrustManager[] createTrustManager(InputStream rootCerts)
53+
throws GeneralSecurityException {
54+
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
55+
try {
56+
ks.load(null, null);
57+
} catch (IOException ex) {
58+
// Shouldn't really happen, as we're not loading any data.
59+
throw new GeneralSecurityException(ex);
60+
}
61+
X509Certificate[] certs = CertificateUtils.getX509Certificates(rootCerts);
62+
for (X509Certificate cert : certs) {
63+
X500Principal principal = cert.getSubjectX500Principal();
64+
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
65+
}
66+
67+
TrustManagerFactory trustManagerFactory =
68+
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
69+
trustManagerFactory.init(ks);
70+
return trustManagerFactory.getTrustManagers();
71+
}
72+
73+
private static X509Certificate[] getX509Certificates(InputStream inputStream)
74+
throws CertificateException {
75+
CertificateFactory factory = CertificateFactory.getInstance("X.509");
76+
Collection<? extends Certificate> certs = factory.generateCertificates(inputStream);
77+
return certs.toArray(new X509Certificate[0]);
78+
}
79+
}

okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java

Lines changed: 9 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,13 @@
1616

1717
package io.grpc.okhttp;
1818

19-
import static com.google.common.base.Preconditions.checkNotNull;
20-
import static io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
21-
import static io.grpc.internal.GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED;
22-
2319
import com.google.common.annotations.VisibleForTesting;
2420
import com.google.common.base.Preconditions;
25-
import io.grpc.CallCredentials;
26-
import io.grpc.ChannelCredentials;
27-
import io.grpc.ChannelLogger;
28-
import io.grpc.ChoiceChannelCredentials;
29-
import io.grpc.CompositeCallCredentials;
30-
import io.grpc.CompositeChannelCredentials;
31-
import io.grpc.ExperimentalApi;
32-
import io.grpc.ForwardingChannelBuilder2;
33-
import io.grpc.InsecureChannelCredentials;
34-
import io.grpc.Internal;
35-
import io.grpc.ManagedChannelBuilder;
36-
import io.grpc.TlsChannelCredentials;
37-
import io.grpc.internal.AtomicBackoff;
38-
import io.grpc.internal.ClientTransportFactory;
39-
import io.grpc.internal.ConnectionClientTransport;
40-
import io.grpc.internal.FixedObjectPool;
41-
import io.grpc.internal.GrpcUtil;
42-
import io.grpc.internal.KeepAliveManager;
43-
import io.grpc.internal.ManagedChannelImplBuilder;
21+
import io.grpc.*;
22+
import io.grpc.internal.*;
4423
import io.grpc.internal.ManagedChannelImplBuilder.ChannelBuilderDefaultPortProvider;
4524
import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder;
46-
import io.grpc.internal.ObjectPool;
4725
import io.grpc.internal.SharedResourceHolder.Resource;
48-
import io.grpc.internal.SharedResourcePool;
49-
import io.grpc.internal.TransportTracer;
5026
import io.grpc.internal.TransportTracer.Factory;
5127
import io.grpc.okhttp.internal.CipherSuite;
5228
import io.grpc.okhttp.internal.ConnectionSpec;
@@ -66,24 +42,18 @@
6642
import java.util.Collections;
6743
import java.util.EnumSet;
6844
import java.util.Set;
69-
import java.util.concurrent.Executor;
70-
import java.util.concurrent.ExecutorService;
71-
import java.util.concurrent.Executors;
72-
import java.util.concurrent.ScheduledExecutorService;
73-
import java.util.concurrent.TimeUnit;
45+
import java.util.concurrent.*;
7446
import java.util.logging.Level;
7547
import java.util.logging.Logger;
7648
import javax.annotation.CheckReturnValue;
7749
import javax.annotation.Nullable;
7850
import javax.net.SocketFactory;
79-
import javax.net.ssl.HostnameVerifier;
80-
import javax.net.ssl.KeyManager;
81-
import javax.net.ssl.KeyManagerFactory;
82-
import javax.net.ssl.SSLContext;
83-
import javax.net.ssl.SSLSocketFactory;
84-
import javax.net.ssl.TrustManager;
85-
import javax.net.ssl.TrustManagerFactory;
86-
import javax.security.auth.x500.X500Principal;
51+
import javax.net.ssl.*;
52+
53+
import static com.google.common.base.Preconditions.checkNotNull;
54+
import static io.grpc.internal.CertificateUtils.createTrustManager;
55+
import static io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
56+
import static io.grpc.internal.GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED;
8757

8858
/** Convenience class for building channels with the OkHttp transport. */
8959
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1785")
@@ -707,35 +677,6 @@ static KeyManager[] createKeyManager(InputStream certChain, InputStream privateK
707677
return keyManagerFactory.getKeyManagers();
708678
}
709679

710-
static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException {
711-
InputStream rootCertsStream = new ByteArrayInputStream(rootCerts);
712-
try {
713-
return createTrustManager(rootCertsStream);
714-
} finally {
715-
GrpcUtil.closeQuietly(rootCertsStream);
716-
}
717-
}
718-
719-
static TrustManager[] createTrustManager(InputStream rootCerts) throws GeneralSecurityException {
720-
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
721-
try {
722-
ks.load(null, null);
723-
} catch (IOException ex) {
724-
// Shouldn't really happen, as we're not loading any data.
725-
throw new GeneralSecurityException(ex);
726-
}
727-
X509Certificate[] certs = CertificateUtils.getX509Certificates(rootCerts);
728-
for (X509Certificate cert : certs) {
729-
X500Principal principal = cert.getSubjectX500Principal();
730-
ks.setCertificateEntry(principal.getName("RFC2253"), cert);
731-
}
732-
733-
TrustManagerFactory trustManagerFactory =
734-
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
735-
trustManagerFactory.init(ks);
736-
return trustManagerFactory.getTrustManagers();
737-
}
738-
739680
static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
740681
return Collections.singleton(InetSocketAddress.class);
741682
}

okhttp/src/main/java/io/grpc/okhttp/OkHttpClientTransport.java

Lines changed: 56 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@
7272
import io.grpc.okhttp.internal.framed.Variant;
7373
import io.grpc.okhttp.internal.proxy.HttpUrl;
7474
import io.grpc.okhttp.internal.proxy.Request;
75-
import io.grpc.util.CertificateUtils;
7675
import io.perfmark.PerfMark;
77-
import java.io.ByteArrayInputStream;
7876
import java.io.EOFException;
7977
import java.io.IOException;
8078
import java.net.InetSocketAddress;
@@ -86,20 +84,8 @@
8684
import java.security.cert.Certificate;
8785
import java.security.cert.CertificateException;
8886
import java.security.cert.X509Certificate;
89-
import java.util.Arrays;
90-
import java.util.Collections;
91-
import java.util.Deque;
92-
import java.util.EnumMap;
93-
import java.util.HashMap;
94-
import java.util.Iterator;
95-
import java.util.LinkedList;
96-
import java.util.List;
97-
import java.util.Locale;
98-
import java.util.Map;
99-
import java.util.Optional;
100-
import java.util.Random;
87+
import java.util.*;
10188
import java.util.concurrent.BrokenBarrierException;
102-
import java.util.concurrent.ConcurrentHashMap;
10389
import java.util.concurrent.CountDownLatch;
10490
import java.util.concurrent.CyclicBarrier;
10591
import java.util.concurrent.Executor;
@@ -231,8 +217,13 @@ private static Map<ErrorCode, Status> buildErrorCodeToStatusMap() {
231217
private final boolean useGetForSafeMethods;
232218
@GuardedBy("lock")
233219
private final TransportTracer transportTracer;
234-
private final ConcurrentHashMap<String, Boolean> authorityVerificationStatuses =
235-
new ConcurrentHashMap<>();
220+
private final LinkedHashMap<String, Status> peerVerificationResults =
221+
new LinkedHashMap<String, Status>() {
222+
@Override
223+
protected boolean removeEldestEntry(Map.Entry<String, Status> eldest) {
224+
return size() > 100;
225+
}
226+
};
236227

237228
@GuardedBy("lock")
238229
private final InUseStateAggregator<OkHttpClientStream> inUseState =
@@ -432,45 +423,51 @@ public ClientStream newStream(
432423
StatsTraceContext.newClientContext(tracers, getAttributes(), headers);
433424
if (socket instanceof SSLSocket && callOptions.getAuthority() != null
434425
&& channelCredentials != null && channelCredentials instanceof TlsChannelCredentials) {
435-
if (hostnameVerifier != null) {
436-
hostnameVerifier.verify(callOptions.getAuthority(), ((SSLSocket) socket).getSession());
437-
}
438-
boolean isAuthorityValid;
439-
if (authorityVerificationStatuses.containsKey(callOptions.getAuthority())) {
440-
isAuthorityValid = authorityVerificationStatuses.get(callOptions.getAuthority());
426+
Status peerVerificationStatus;
427+
if (peerVerificationResults.containsKey(callOptions.getAuthority())) {
428+
peerVerificationStatus = peerVerificationResults.get(callOptions.getAuthority());
441429
} else {
442-
Optional<TrustManager> x509ExtendedTrustManager;
443-
try {
444-
x509ExtendedTrustManager = getX509ExtendedTrustManager(
445-
(TlsChannelCredentials) channelCredentials);
446-
} catch (GeneralSecurityException e) {
447-
log.log(Level.FINE, "Failure getting X509ExtendedTrustManager from TlsCredentials", e);
448-
return new FailingClientStream(Status.INTERNAL.withDescription(
449-
"Failure getting X509ExtendedTrustManager from TlsCredentials"),
450-
tracers);
451-
}
452-
if (!x509ExtendedTrustManager.isPresent()) {
453-
return new FailingClientStream(Status.INTERNAL.withDescription(
454-
"Can't allow authority override in rpc when X509ExtendedTrustManager is not "
455-
+ "available"), tracers);
456-
}
457-
try {
458-
Certificate[] peerCertificates = sslSession.getPeerCertificates();
459-
X509Certificate[] x509PeerCertificates = new X509Certificate[peerCertificates.length];
460-
for (int i = 0; i < peerCertificates.length; i++) {
461-
x509PeerCertificates[i] = (X509Certificate) peerCertificates[i];
430+
if (hostnameVerifier != null &&
431+
!hostnameVerifier.verify(callOptions.getAuthority(),
432+
((SSLSocket) socket).getSession())) {
433+
peerVerificationStatus = Status.UNAVAILABLE.withDescription(
434+
String.format("HostNameVerifier verification failed for authority '%s'.",
435+
callOptions.getAuthority()));
436+
} else {
437+
Optional<TrustManager> x509ExtendedTrustManager;
438+
try {
439+
x509ExtendedTrustManager = getX509ExtendedTrustManager(
440+
(TlsChannelCredentials) channelCredentials);
441+
} catch (GeneralSecurityException e) {
442+
return new FailingClientStream(Status.UNAVAILABLE.withDescription(
443+
"Failure getting X509ExtendedTrustManager from TlsCredentials").withCause(e),
444+
tracers);
445+
}
446+
if (!x509ExtendedTrustManager.isPresent()) {
447+
return new FailingClientStream(Status.UNAVAILABLE.withDescription(
448+
"Can't allow authority override in rpc when X509ExtendedTrustManager is not "
449+
+ "available"), tracers);
450+
}
451+
try {
452+
Certificate[] peerCertificates = sslSession.getPeerCertificates();
453+
X509Certificate[] x509PeerCertificates = new X509Certificate[peerCertificates.length];
454+
for (int i = 0; i < peerCertificates.length; i++) {
455+
x509PeerCertificates[i] = (X509Certificate) peerCertificates[i];
456+
}
457+
((X509ExtendedTrustManager) x509ExtendedTrustManager.get()).checkServerTrusted(
458+
x509PeerCertificates, "RSA",
459+
new SslSocketWrapper((SSLSocket) socket, callOptions.getAuthority()));
460+
peerVerificationStatus = Status.OK;
461+
} catch (SSLPeerUnverifiedException | CertificateException e) {
462+
peerVerificationStatus = Status.INTERNAL.withDescription(
463+
String.format("Failure in verifying authority '%s' against peer",
464+
callOptions.getAuthority())).withCause(e);
462465
}
463-
((X509ExtendedTrustManager) x509ExtendedTrustManager.get()).checkServerTrusted(
464-
x509PeerCertificates, "RSA",
465-
new SslSocketWrapper((SSLSocket) socket, callOptions.getAuthority()));
466-
authorityVerificationStatuses.put(callOptions.getAuthority(), true);
467-
} catch (SSLPeerUnverifiedException | CertificateException e) {
468-
log.log(Level.FINE, "Failure in verifying authority against peer", e);
469-
authorityVerificationStatuses.put(callOptions.getAuthority(), false);
470-
return new FailingClientStream(Status.INTERNAL.withDescription(
471-
"Failure in verifying authority against peer"),
472-
tracers);
473466
}
467+
peerVerificationResults.put(callOptions.getAuthority(), peerVerificationStatus);
468+
}
469+
if (!peerVerificationStatus.isOk()) {
470+
return new FailingClientStream(peerVerificationStatus, tracers);
474471
}
475472
}
476473
// FIXME: it is likely wrong to pass the transportTracer here as it'll exit the lock's scope
@@ -495,21 +492,19 @@ public ClientStream newStream(
495492

496493
private Optional<TrustManager> getX509ExtendedTrustManager(TlsChannelCredentials tlsCreds)
497494
throws GeneralSecurityException {
498-
Optional<TrustManager> x509ExtendedTrustManager;
495+
TrustManager[] tm = null;
496+
// Using the same way of creating TrustManager from {@link OkHttpChannelBuilder#sslSocketFactoryFrom}.
499497
if (tlsCreds.getTrustManagers() != null) {
500-
x509ExtendedTrustManager = tlsCreds.getTrustManagers().stream().filter(
501-
trustManager -> trustManager instanceof X509ExtendedTrustManager).findFirst();
498+
tm = tlsCreds.getTrustManagers().toArray(new TrustManager[0]);
502499
} else if (tlsCreds.getRootCertificates() != null) {
503-
x509ExtendedTrustManager = CertificateUtils.getX509ExtendedTrustManager(
504-
new ByteArrayInputStream(tlsCreds.getRootCertificates()));
500+
tm = io.grpc.internal.CertificateUtils.createTrustManager(tlsCreds.getRootCertificates());
505501
} else { // else use system default
506502
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
507503
TrustManagerFactory.getDefaultAlgorithm());
508504
tmf.init((KeyStore) null);
509-
x509ExtendedTrustManager = Arrays.stream(tmf.getTrustManagers())
510-
.filter(trustManager -> trustManager instanceof X509ExtendedTrustManager).findFirst();
505+
tm = tmf.getTrustManagers();
511506
}
512-
return x509ExtendedTrustManager;
507+
return Arrays.stream(tm).filter(trustManager -> trustManager instanceof X509ExtendedTrustManager).findFirst();
513508
}
514509

515510
@GuardedBy("lock")

0 commit comments

Comments
 (0)