1 /* 2 * Copyright 2014 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.okhttp; 18 19 import com.google.common.annotations.VisibleForTesting; 20 import com.google.common.base.Preconditions; 21 import io.grpc.okhttp.internal.ConnectionSpec; 22 import io.grpc.okhttp.internal.OkHostnameVerifier; 23 import io.grpc.okhttp.internal.Protocol; 24 import java.io.IOException; 25 import java.net.Socket; 26 import java.util.Arrays; 27 import java.util.Collections; 28 import java.util.List; 29 import javax.net.ssl.HostnameVerifier; 30 import javax.net.ssl.SSLPeerUnverifiedException; 31 import javax.net.ssl.SSLSocket; 32 import javax.net.ssl.SSLSocketFactory; 33 34 /** 35 * A helper class that located in package com.squareup.okhttp, so that we can use OkHttp internals 36 * to do TLS upgrading. 37 */ 38 final class OkHttpTlsUpgrader { 39 40 /* 41 * List of ALPN/NPN protocols in order of preference. GRPC_EXP requires that 42 * HTTP_2 be present and that GRPC_EXP should be preferenced over HTTP_2. 43 */ 44 @VisibleForTesting 45 static final List<Protocol> TLS_PROTOCOLS = 46 Collections.unmodifiableList(Arrays.asList(Protocol.GRPC_EXP, Protocol.HTTP_2)); 47 48 /** 49 * Upgrades given Socket to be a SSLSocket. 50 * 51 * @throws IOException if an IO error was encountered during the upgrade handshake. 52 * @throws RuntimeException if the upgrade negotiation failed. 53 */ upgrade(SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier, Socket socket, String host, int port, ConnectionSpec spec)54 public static SSLSocket upgrade(SSLSocketFactory sslSocketFactory, 55 HostnameVerifier hostnameVerifier, Socket socket, String host, int port, 56 ConnectionSpec spec) throws IOException { 57 Preconditions.checkNotNull(sslSocketFactory, "sslSocketFactory"); 58 Preconditions.checkNotNull(socket, "socket"); 59 Preconditions.checkNotNull(spec, "spec"); 60 SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket( 61 socket, host, port, true /* auto close */); 62 spec.apply(sslSocket, false); 63 String negotiatedProtocol = OkHttpProtocolNegotiator.get().negotiate( 64 sslSocket, host, spec.supportsTlsExtensions() ? TLS_PROTOCOLS : null); 65 Preconditions.checkState( 66 TLS_PROTOCOLS.contains(Protocol.get(negotiatedProtocol)), 67 "Only " + TLS_PROTOCOLS + " are supported, but negotiated protocol is %s", 68 negotiatedProtocol); 69 70 if (hostnameVerifier == null) { 71 hostnameVerifier = OkHostnameVerifier.INSTANCE; 72 } 73 if (!hostnameVerifier.verify(canonicalizeHost(host), sslSocket.getSession())) { 74 throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host); 75 } 76 return sslSocket; 77 } 78 79 /** 80 * Converts a host from URI to X509 format. 81 * 82 * <p>IPv6 host addresses derived from URIs are enclosed in square brackets per RFC2732, but 83 * omit these brackets in X509 certificate subjectAltName extensions per RFC5280. 84 * 85 * @see <a href="https://www.ietf.org/rfc/rfc2732.txt">RFC2732</a> 86 * @see <a href="https://tools.ietf.org/html/rfc5280#section-4.2.1.6">RFC5280</a> 87 * 88 * @return {@param host} in a form consistent with X509 certificates 89 */ 90 @VisibleForTesting canonicalizeHost(String host)91 static String canonicalizeHost(String host) { 92 if (host.startsWith("[") && host.endsWith("]")) { 93 return host.substring(1, host.length() - 1); 94 } 95 return host; 96 } 97 } 98