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.internal.testing; 18 19 import com.google.common.base.Throwables; 20 import io.grpc.internal.ConscryptLoader; 21 import io.grpc.testing.TlsTesting; 22 import java.io.BufferedInputStream; 23 import java.io.BufferedOutputStream; 24 import java.io.File; 25 import java.io.FileInputStream; 26 import java.io.FileOutputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.OutputStream; 30 import java.net.InetAddress; 31 import java.net.InetSocketAddress; 32 import java.net.UnknownHostException; 33 import java.security.KeyStore; 34 import java.security.Provider; 35 import java.security.Security; 36 import java.security.cert.CertificateException; 37 import java.security.cert.CertificateFactory; 38 import java.security.cert.X509Certificate; 39 import java.util.ArrayList; 40 import java.util.List; 41 import javax.net.ssl.SSLContext; 42 import javax.net.ssl.SSLSocketFactory; 43 import javax.net.ssl.TrustManagerFactory; 44 import javax.security.auth.x500.X500Principal; 45 46 /** 47 * Internal utility functions useful for writing tests. 48 */ 49 public class TestUtils { 50 public static final String TEST_SERVER_HOST = "foo.test.google.fr"; 51 52 /** 53 * Creates a new {@link InetSocketAddress} that overrides the host with {@link #TEST_SERVER_HOST}. 54 */ testServerAddress(String host, int port)55 public static InetSocketAddress testServerAddress(String host, int port) { 56 try { 57 InetAddress inetAddress = InetAddress.getByName(host); 58 inetAddress = InetAddress.getByAddress(TEST_SERVER_HOST, inetAddress.getAddress()); 59 return new InetSocketAddress(inetAddress, port); 60 } catch (UnknownHostException e) { 61 throw new RuntimeException(e); 62 } 63 } 64 65 /** 66 * Creates a new {@link InetSocketAddress} on localhost that overrides the host with 67 * {@link #TEST_SERVER_HOST}. 68 */ testServerAddress(InetSocketAddress originalSockAddr)69 public static InetSocketAddress testServerAddress(InetSocketAddress originalSockAddr) { 70 try { 71 InetAddress inetAddress = InetAddress.getByName("localhost"); 72 inetAddress = InetAddress.getByAddress(TEST_SERVER_HOST, inetAddress.getAddress()); 73 return new InetSocketAddress(inetAddress, originalSockAddr.getPort()); 74 } catch (UnknownHostException e) { 75 throw new RuntimeException(e); 76 } 77 } 78 79 /** 80 * Creates a new list of {@link InetSocketAddress} on localhost that overrides the host with 81 * {@link #TEST_SERVER_HOST}. 82 */ testServerAddresses(InetSocketAddress... originalSockAddr)83 public static List<InetSocketAddress> testServerAddresses(InetSocketAddress... originalSockAddr) { 84 try { 85 InetAddress inetAddress = InetAddress.getByName("localhost"); 86 inetAddress = InetAddress.getByAddress(TEST_SERVER_HOST, inetAddress.getAddress()); 87 List<InetSocketAddress> addresses = new ArrayList<>(); 88 for (InetSocketAddress orig: originalSockAddr) { 89 addresses.add(new InetSocketAddress(inetAddress, orig.getPort())); 90 } 91 return addresses; 92 } catch (UnknownHostException e) { 93 throw new RuntimeException(e); 94 } 95 } 96 97 /** 98 * Saves a file from the classpath resources in src/main/resources/certs as a file on the 99 * filesystem. 100 * 101 * @param name name of a file in src/main/resources/certs. 102 */ loadCert(String name)103 public static File loadCert(String name) throws IOException { 104 InputStream in = new BufferedInputStream(TlsTesting.loadCert(name)); 105 File tmpFile = File.createTempFile(name, ""); 106 tmpFile.deleteOnExit(); 107 108 OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile)); 109 try { 110 int b; 111 while ((b = in.read()) != -1) { 112 os.write(b); 113 } 114 os.flush(); 115 } finally { 116 in.close(); 117 os.close(); 118 } 119 120 return tmpFile; 121 } 122 123 /** 124 * Loads an X.509 certificate from the classpath resources in src/main/resources/certs. 125 * 126 * @param fileName name of a file in src/main/resources/certs. 127 */ loadX509Cert(String fileName)128 public static X509Certificate loadX509Cert(String fileName) 129 throws CertificateException, IOException { 130 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 131 132 InputStream in = TlsTesting.loadCert(fileName); 133 try { 134 return (X509Certificate) cf.generateCertificate(in); 135 } finally { 136 in.close(); 137 } 138 } 139 140 private static boolean conscryptInstallAttempted; 141 142 /** 143 * Add Conscrypt to the list of security providers, if it is available. If it appears to be 144 * available but fails to load, this method will throw an exception. Since the list of security 145 * providers is static, this method does nothing if the provider is not available or succeeded 146 * previously. 147 */ installConscryptIfAvailable()148 public static void installConscryptIfAvailable() { 149 if (conscryptInstallAttempted) { 150 return; 151 } 152 // Conscrypt-based I/O (like used in OkHttp) breaks on Windows. 153 // https://github.com/google/conscrypt/issues/444 154 if (System.mapLibraryName("test").endsWith(".dll")) { 155 conscryptInstallAttempted = true; 156 return; 157 } 158 // Concrypt native libraries are not available for ARM architectures. 159 String osArch = System.getProperty("os.arch", ""); 160 if (osArch.startsWith("aarch") || osArch.startsWith("arm")) { 161 conscryptInstallAttempted = true; 162 return; 163 } 164 if (!ConscryptLoader.isPresent()) { 165 conscryptInstallAttempted = true; 166 return; 167 } 168 Provider provider; 169 try { 170 provider = ConscryptLoader.newProvider(); 171 } catch (Throwable t) { 172 Throwable root = Throwables.getRootCause(t); 173 // Conscrypt uses a newer version of glibc than available on RHEL 6 174 if (root instanceof UnsatisfiedLinkError && root.getMessage() != null 175 && root.getMessage().contains("GLIBC_2.14")) { 176 conscryptInstallAttempted = true; 177 return; 178 } 179 throw new RuntimeException("Could not create Conscrypt provider", t); 180 } 181 Security.addProvider(provider); 182 conscryptInstallAttempted = true; 183 } 184 185 /** 186 * Creates an SSLSocketFactory which contains {@code certChainFile} as its only root certificate. 187 */ newSslSocketFactoryForCa(Provider provider, File certChainFile)188 public static SSLSocketFactory newSslSocketFactoryForCa(Provider provider, 189 File certChainFile) throws Exception { 190 KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 191 ks.load(null, null); 192 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 193 BufferedInputStream in = new BufferedInputStream(new FileInputStream(certChainFile)); 194 try { 195 X509Certificate cert = (X509Certificate) cf.generateCertificate(in); 196 X500Principal principal = cert.getSubjectX500Principal(); 197 ks.setCertificateEntry(principal.getName("RFC2253"), cert); 198 } finally { 199 in.close(); 200 } 201 202 // Set up trust manager factory to use our key store. 203 TrustManagerFactory trustManagerFactory = 204 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 205 trustManagerFactory.init(ks); 206 SSLContext context = SSLContext.getInstance("TLS", provider); 207 context.init(null, trustManagerFactory.getTrustManagers(), null); 208 return context.getSocketFactory(); 209 } 210 TestUtils()211 private TestUtils() {} 212 } 213