• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package org.conscrypt;
19 
20 import java.security.KeyManagementException;
21 import java.security.KeyStore;
22 import java.security.KeyStoreException;
23 import java.security.NoSuchAlgorithmException;
24 import java.security.SecureRandom;
25 import java.security.UnrecoverableKeyException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import javax.crypto.SecretKey;
29 import javax.net.ssl.KeyManager;
30 import javax.net.ssl.KeyManagerFactory;
31 import javax.net.ssl.TrustManager;
32 import javax.net.ssl.TrustManagerFactory;
33 import javax.net.ssl.X509ExtendedKeyManager;
34 import javax.net.ssl.X509KeyManager;
35 import javax.net.ssl.X509TrustManager;
36 import javax.security.auth.x500.X500Principal;
37 
38 /**
39  * The instances of this class encapsulate all the info
40  * about enabled cipher suites and protocols,
41  * as well as the information about client/server mode of
42  * ssl socket, whether it require/want client authentication or not,
43  * and controls whether new SSL sessions may be established by this
44  * socket or not.
45  */
46 final class SSLParametersImpl implements Cloneable {
47 
48     // default source of X.509 certificate based authentication keys
49     private static volatile X509KeyManager defaultX509KeyManager;
50     // default source of X.509 certificate based authentication trust decisions
51     private static volatile X509TrustManager defaultX509TrustManager;
52     // default SSL parameters
53     private static volatile SSLParametersImpl defaultParameters;
54 
55     // client session context contains the set of reusable
56     // client-side SSL sessions
57     private final ClientSessionContext clientSessionContext;
58     // server session context contains the set of reusable
59     // server-side SSL sessions
60     private final ServerSessionContext serverSessionContext;
61     // source of X.509 certificate based authentication keys or null if not provided
62     private final X509KeyManager x509KeyManager;
63     // source of Pre-Shared Key (PSK) authentication keys or null if not provided.
64     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
65     private final PSKKeyManager pskKeyManager;
66     // source of X.509 certificate based authentication trust decisions or null if not provided
67     private final X509TrustManager x509TrustManager;
68     // source of random numbers
69     private SecureRandom secureRandom;
70 
71     // protocols enabled for SSL connection
72     String[] enabledProtocols;
73     // set to indicate when obsolete protocols are filtered
74     boolean isEnabledProtocolsFiltered;
75     // cipher suites enabled for SSL connection
76     String[] enabledCipherSuites;
77 
78     // if the peer with this parameters tuned to work in client mode
79     private boolean client_mode = true;
80     // if the peer with this parameters tuned to require client authentication
81     private boolean need_client_auth = false;
82     // if the peer with this parameters tuned to request client authentication
83     private boolean want_client_auth = false;
84     // if the peer with this parameters allowed to cteate new SSL session
85     private boolean enable_session_creation = true;
86     // Endpoint identification algorithm (e.g., HTTPS)
87     private String endpointIdentificationAlgorithm;
88     // Whether to use the local cipher suites order
89     private boolean useCipherSuitesOrder;
90 
91     // client-side only, bypasses the property based configuration, used for tests
92     private boolean ctVerificationEnabled;
93 
94     // server-side only. SCT and OCSP data to send to clients which request it
95     byte[] sctExtension;
96     byte[] ocspResponse;
97 
98     byte[] alpnProtocols;
99     boolean useSessionTickets;
100     private Boolean useSni;
101 
102     /**
103      * Whether the TLS Channel ID extension is enabled. This field is
104      * server-side only.
105      */
106     boolean channelIdEnabled;
107 
108     /**
109      * Initializes the parameters. Naturally this constructor is used
110      * in SSLContextImpl.engineInit method which directly passes its
111      * parameters. In other words this constructor holds all
112      * the functionality provided by SSLContext.init method.
113      * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
114      * SecureRandom)} for more information
115      */
SSLParametersImpl(KeyManager[] kms, TrustManager[] tms, SecureRandom sr, ClientSessionContext clientSessionContext, ServerSessionContext serverSessionContext, String[] protocols)116     SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
117             SecureRandom sr, ClientSessionContext clientSessionContext,
118             ServerSessionContext serverSessionContext, String[] protocols)
119             throws KeyManagementException {
120         this.serverSessionContext = serverSessionContext;
121         this.clientSessionContext = clientSessionContext;
122 
123         // initialize key managers
124         if (kms == null) {
125             x509KeyManager = getDefaultX509KeyManager();
126             // There's no default PSK key manager
127             pskKeyManager = null;
128         } else {
129             x509KeyManager = findFirstX509KeyManager(kms);
130             pskKeyManager = findFirstPSKKeyManager(kms);
131         }
132 
133         // initialize x509TrustManager
134         if (tms == null) {
135             x509TrustManager = getDefaultX509TrustManager();
136         } else {
137             x509TrustManager = findFirstX509TrustManager(tms);
138         }
139 
140         // initialize secure random
141         // We simply use the SecureRandom passed in by the caller. If it's
142         // null, we don't replace it by a new instance. The native code below
143         // then directly accesses /dev/urandom. Not the most elegant solution,
144         // but faster than going through the SecureRandom object.
145         secureRandom = sr;
146 
147         // initialize the list of cipher suites and protocols enabled by default
148         enabledProtocols = NativeCrypto.checkEnabledProtocols(
149                 protocols == null ? NativeCrypto.DEFAULT_PROTOCOLS : protocols).clone();
150         boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null);
151         boolean pskCipherSuitesNeeded = pskKeyManager != null;
152         enabledCipherSuites = getDefaultCipherSuites(
153                 x509CipherSuitesNeeded, pskCipherSuitesNeeded);
154     }
155 
getDefault()156     static SSLParametersImpl getDefault() throws KeyManagementException {
157         SSLParametersImpl result = defaultParameters;
158         if (result == null) {
159             // single-check idiom
160             defaultParameters = result = new SSLParametersImpl(null,
161                                                                null,
162                                                                null,
163                                                                new ClientSessionContext(),
164                                                                new ServerSessionContext(),
165                                                                null);
166         }
167         return (SSLParametersImpl) result.clone();
168     }
169 
170     /**
171      * Returns the appropriate session context.
172      */
getSessionContext()173     AbstractSessionContext getSessionContext() {
174         return client_mode ? clientSessionContext : serverSessionContext;
175     }
176 
177     /**
178      * @return client session context
179      */
getClientSessionContext()180     ClientSessionContext getClientSessionContext() {
181         return clientSessionContext;
182     }
183 
184     /**
185      * @return X.509 key manager or {@code null} for none.
186      */
getX509KeyManager()187     X509KeyManager getX509KeyManager() {
188         return x509KeyManager;
189     }
190 
191     /**
192      * @return Pre-Shared Key (PSK) key manager or {@code null} for none.
193      */
194     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
getPSKKeyManager()195     PSKKeyManager getPSKKeyManager() {
196         return pskKeyManager;
197     }
198 
199     /**
200      * @return X.509 trust manager or {@code null} for none.
201      */
getX509TrustManager()202     X509TrustManager getX509TrustManager() {
203         return x509TrustManager;
204     }
205 
206     /**
207      * @return the names of enabled cipher suites
208      */
getEnabledCipherSuites()209     String[] getEnabledCipherSuites() {
210         return enabledCipherSuites.clone();
211     }
212 
213     /**
214      * Sets the enabled cipher suites after filtering through OpenSSL.
215      */
setEnabledCipherSuites(String[] cipherSuites)216     void setEnabledCipherSuites(String[] cipherSuites) {
217         enabledCipherSuites = NativeCrypto.checkEnabledCipherSuites(cipherSuites).clone();
218     }
219 
220     /**
221      * @return the set of enabled protocols
222      */
getEnabledProtocols()223     String[] getEnabledProtocols() {
224         return enabledProtocols.clone();
225     }
226 
227     /**
228      * Sets the list of available protocols for use in SSL connection.
229      * @throws IllegalArgumentException if {@code protocols == null}
230      */
setEnabledProtocols(String[] protocols)231     void setEnabledProtocols(String[] protocols) {
232         if (protocols == null) {
233             throw new IllegalArgumentException("protocols == null");
234         }
235         String[] filteredProtocols =
236                 filterFromProtocols(protocols, NativeCrypto.OBSOLETE_PROTOCOL_SSLV3);
237         isEnabledProtocolsFiltered = protocols.length != filteredProtocols.length;
238         enabledProtocols = NativeCrypto.checkEnabledProtocols(filteredProtocols).clone();
239     }
240 
241     /**
242      * Sets the list of ALPN protocols. This method internally converts the protocols to their
243      * wire-format form.
244      *
245      * @param alpnProtocols the list of ALPN protocols
246      * @see #setAlpnProtocols(byte[])
247      */
setAlpnProtocols(String[] alpnProtocols)248     void setAlpnProtocols(String[] alpnProtocols) {
249         setAlpnProtocols(SSLUtils.toLengthPrefixedList(alpnProtocols));
250     }
251 
252     /**
253      * Alternate version of {@link #setAlpnProtocols(String[])} that directly sets the list of
254      * ALPN in the wire-format form used by BoringSSL (length-prefixed 8-bit strings).
255      * Requires that all strings be encoded with US-ASCII.
256      *
257      * @param alpnProtocols the encoded form of the ALPN protocol list
258      * @see #setAlpnProtocols(String[])
259      */
setAlpnProtocols(byte[] alpnProtocols)260     void setAlpnProtocols(byte[] alpnProtocols) {
261         if (alpnProtocols != null && alpnProtocols.length == 0) {
262             throw new IllegalArgumentException("alpnProtocols.length == 0");
263         }
264         this.alpnProtocols = alpnProtocols;
265     }
266 
getAlpnProtocols()267     byte[] getAlpnProtocols() {
268         return alpnProtocols;
269     }
270 
271     /**
272      * Tunes the peer holding this parameters to work in client mode.
273      * @param   mode if the peer is configured to work in client mode
274      */
setUseClientMode(boolean mode)275     void setUseClientMode(boolean mode) {
276         client_mode = mode;
277     }
278 
279     /**
280      * Returns the value indicating if the parameters configured to work
281      * in client mode.
282      */
getUseClientMode()283     boolean getUseClientMode() {
284         return client_mode;
285     }
286 
287     /**
288      * Tunes the peer holding this parameters to require client authentication
289      */
setNeedClientAuth(boolean need)290     void setNeedClientAuth(boolean need) {
291         need_client_auth = need;
292         // reset the want_client_auth setting
293         want_client_auth = false;
294     }
295 
296     /**
297      * Returns the value indicating if the peer with this parameters tuned
298      * to require client authentication
299      */
getNeedClientAuth()300     boolean getNeedClientAuth() {
301         return need_client_auth;
302     }
303 
304     /**
305      * Tunes the peer holding this parameters to request client authentication
306      */
setWantClientAuth(boolean want)307     void setWantClientAuth(boolean want) {
308         want_client_auth = want;
309         // reset the need_client_auth setting
310         need_client_auth = false;
311     }
312 
313     /**
314      * Returns the value indicating if the peer with this parameters
315      * tuned to request client authentication
316      */
getWantClientAuth()317     boolean getWantClientAuth() {
318         return want_client_auth;
319     }
320 
321     /**
322      * Allows/disallows the peer holding this parameters to
323      * create new SSL session
324      */
setEnableSessionCreation(boolean flag)325     void setEnableSessionCreation(boolean flag) {
326         enable_session_creation = flag;
327     }
328 
329     /**
330      * Returns the value indicating if the peer with this parameters
331      * allowed to cteate new SSL session
332      */
getEnableSessionCreation()333     boolean getEnableSessionCreation() {
334         return enable_session_creation;
335     }
336 
setUseSessionTickets(boolean useSessionTickets)337     void setUseSessionTickets(boolean useSessionTickets) {
338         this.useSessionTickets = useSessionTickets;
339     }
340 
341     /**
342      * Whether connections using this SSL connection should use the TLS
343      * extension Server Name Indication (SNI).
344      */
setUseSni(boolean flag)345     void setUseSni(boolean flag) {
346         useSni = flag;
347     }
348 
349     /**
350      * Returns whether connections using this SSL connection should use the TLS
351      * extension Server Name Indication (SNI).
352      */
getUseSni()353     boolean getUseSni() {
354         return useSni != null ? useSni : isSniEnabledByDefault();
355     }
356 
357     /**
358      * For testing only.
359      */
setCTVerificationEnabled(boolean enabled)360     void setCTVerificationEnabled(boolean enabled) {
361         ctVerificationEnabled = enabled;
362     }
363 
364     /**
365      * For testing only.
366      */
setSCTExtension(byte[] extension)367     void setSCTExtension(byte[] extension) {
368         sctExtension = extension;
369     }
370 
371     /**
372      * For testing only.
373      */
setOCSPResponse(byte[] response)374     void setOCSPResponse(byte[] response) {
375         ocspResponse = response;
376     }
377 
getOCSPResponse()378     byte[] getOCSPResponse() {
379         return ocspResponse;
380     }
381 
382     /**
383      * This filters {@code obsoleteProtocol} from the list of {@code protocols}
384      * down to help with app compatibility.
385      */
filterFromProtocols(String[] protocols, String obsoleteProtocol)386     private static String[] filterFromProtocols(String[] protocols, String obsoleteProtocol) {
387         if (protocols.length == 1 && obsoleteProtocol.equals(protocols[0])) {
388             return EMPTY_STRING_ARRAY;
389         }
390 
391         ArrayList<String> newProtocols = new ArrayList<String>();
392         for (String protocol : protocols) {
393             if (!obsoleteProtocol.equals(protocol)) {
394                 newProtocols.add(protocol);
395             }
396         }
397         return newProtocols.toArray(EMPTY_STRING_ARRAY);
398     }
399 
400     private static final String[] EMPTY_STRING_ARRAY = new String[0];
401 
402     /**
403      * Returns whether Server Name Indication (SNI) is enabled by default for
404      * sockets. For more information on SNI, see RFC 6066 section 3.
405      */
isSniEnabledByDefault()406     private boolean isSniEnabledByDefault() {
407         try {
408             String enableSNI = System.getProperty("jsse.enableSNIExtension", "true");
409             if ("true".equalsIgnoreCase(enableSNI)) {
410                 return true;
411             } else if ("false".equalsIgnoreCase(enableSNI)) {
412                 return false;
413             } else {
414                 throw new RuntimeException(
415                         "Can only set \"jsse.enableSNIExtension\" to \"true\" or \"false\"");
416             }
417         } catch (SecurityException e) {
418             return true;
419         }
420     }
421 
422     /**
423      * For abstracting the X509KeyManager calls between
424      * {@link X509KeyManager#chooseClientAlias(String[], java.security.Principal[], java.net.Socket)}
425      * and
426      * {@link X509ExtendedKeyManager#chooseEngineClientAlias(String[], java.security.Principal[], javax.net.ssl.SSLEngine)}
427      */
428     interface AliasChooser {
chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers, String[] keyTypes)429         String chooseClientAlias(X509KeyManager keyManager, X500Principal[] issuers,
430                 String[] keyTypes);
431 
chooseServerAlias(X509KeyManager keyManager, String keyType)432         String chooseServerAlias(X509KeyManager keyManager, String keyType);
433     }
434 
435     /**
436      * For abstracting the {@code PSKKeyManager} calls between those taking an {@code SSLSocket} and
437      * those taking an {@code SSLEngine}.
438      */
439     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
440     interface PSKCallbacks {
chooseServerPSKIdentityHint(PSKKeyManager keyManager)441         String chooseServerPSKIdentityHint(PSKKeyManager keyManager);
chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint)442         String chooseClientPSKIdentity(PSKKeyManager keyManager, String identityHint);
getPSKKey(PSKKeyManager keyManager, String identityHint, String identity)443         SecretKey getPSKKey(PSKKeyManager keyManager, String identityHint, String identity);
444     }
445 
446     /**
447      * Returns the clone of this object.
448      * @return the clone.
449      */
450     @Override
clone()451     protected Object clone() {
452         try {
453             return super.clone();
454         } catch (CloneNotSupportedException e) {
455             throw new AssertionError(e);
456         }
457     }
458 
getDefaultX509KeyManager()459     private static X509KeyManager getDefaultX509KeyManager() throws KeyManagementException {
460         X509KeyManager result = defaultX509KeyManager;
461         if (result == null) {
462             // single-check idiom
463             defaultX509KeyManager = result = createDefaultX509KeyManager();
464         }
465         return result;
466     }
createDefaultX509KeyManager()467     private static X509KeyManager createDefaultX509KeyManager() throws KeyManagementException {
468         try {
469             String algorithm = KeyManagerFactory.getDefaultAlgorithm();
470             KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
471             kmf.init(null, null);
472             KeyManager[] kms = kmf.getKeyManagers();
473             X509KeyManager result = findFirstX509KeyManager(kms);
474             if (result == null) {
475                 throw new KeyManagementException("No X509KeyManager among default KeyManagers: "
476                         + Arrays.toString(kms));
477             }
478             return result;
479         } catch (NoSuchAlgorithmException e) {
480             throw new KeyManagementException(e);
481         } catch (KeyStoreException e) {
482             throw new KeyManagementException(e);
483         } catch (UnrecoverableKeyException e) {
484             throw new KeyManagementException(e);
485         }
486     }
487 
488     /**
489      * Finds the first {@link X509KeyManager} element in the provided array.
490      *
491      * @return the first {@code X509KeyManager} or {@code null} if not found.
492      */
findFirstX509KeyManager(KeyManager[] kms)493     private static X509KeyManager findFirstX509KeyManager(KeyManager[] kms) {
494         for (KeyManager km : kms) {
495             if (km instanceof X509KeyManager) {
496                 return (X509KeyManager)km;
497             }
498         }
499         return null;
500     }
501 
502     /**
503      * Finds the first {@link PSKKeyManager} element in the provided array.
504      *
505      * @return the first {@code PSKKeyManager} or {@code null} if not found.
506      */
507     @SuppressWarnings("deprecation") // PSKKeyManager is deprecated, but in our own package
findFirstPSKKeyManager(KeyManager[] kms)508     private static PSKKeyManager findFirstPSKKeyManager(KeyManager[] kms) {
509         for (KeyManager km : kms) {
510             if (km instanceof PSKKeyManager) {
511                 return (PSKKeyManager)km;
512             } else if (km != null) {
513                 try {
514                     return DuckTypedPSKKeyManager.getInstance(km);
515                 } catch (NoSuchMethodException ignored) {}
516             }
517         }
518         return null;
519     }
520 
521     /**
522      * Gets the default X.509 trust manager.
523      */
getDefaultX509TrustManager()524     static X509TrustManager getDefaultX509TrustManager()
525             throws KeyManagementException {
526         X509TrustManager result = defaultX509TrustManager;
527         if (result == null) {
528             // single-check idiom
529             defaultX509TrustManager = result = createDefaultX509TrustManager();
530         }
531         return result;
532     }
533 
createDefaultX509TrustManager()534     private static X509TrustManager createDefaultX509TrustManager()
535             throws KeyManagementException {
536         try {
537             String algorithm = TrustManagerFactory.getDefaultAlgorithm();
538             TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
539             tmf.init((KeyStore) null);
540             TrustManager[] tms = tmf.getTrustManagers();
541             X509TrustManager trustManager = findFirstX509TrustManager(tms);
542             if (trustManager == null) {
543                 throw new KeyManagementException(
544                         "No X509TrustManager in among default TrustManagers: "
545                                 + Arrays.toString(tms));
546             }
547             return trustManager;
548         } catch (NoSuchAlgorithmException e) {
549             throw new KeyManagementException(e);
550         } catch (KeyStoreException e) {
551             throw new KeyManagementException(e);
552         }
553     }
554 
555     /**
556      * Finds the first {@link X509TrustManager} element in the provided array.
557      *
558      * @return the first {@code X509ExtendedTrustManager} or
559      *         {@code X509TrustManager} or {@code null} if not found.
560      */
findFirstX509TrustManager(TrustManager[] tms)561     private static X509TrustManager findFirstX509TrustManager(TrustManager[] tms) {
562         for (TrustManager tm : tms) {
563             if (tm instanceof X509TrustManager) {
564                 return (X509TrustManager) tm;
565             }
566         }
567         return null;
568     }
569 
getEndpointIdentificationAlgorithm()570     String getEndpointIdentificationAlgorithm() {
571         return endpointIdentificationAlgorithm;
572     }
573 
setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm)574     void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
575         this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
576     }
577 
getUseCipherSuitesOrder()578     boolean getUseCipherSuitesOrder() {
579         return useCipherSuitesOrder;
580     }
581 
setUseCipherSuitesOrder(boolean useCipherSuitesOrder)582     void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) {
583         this.useCipherSuitesOrder = useCipherSuitesOrder;
584     }
585 
getDefaultCipherSuites( boolean x509CipherSuitesNeeded, boolean pskCipherSuitesNeeded)586     private static String[] getDefaultCipherSuites(
587             boolean x509CipherSuitesNeeded,
588             boolean pskCipherSuitesNeeded) {
589         if (x509CipherSuitesNeeded) {
590             // X.509 based cipher suites need to be listed.
591             if (pskCipherSuitesNeeded) {
592                 // Both X.509 and PSK based cipher suites need to be listed. Because TLS-PSK is not
593                 // normally used, we assume that when PSK cipher suites are requested here they
594                 // should be preferred over other cipher suites. Thus, we give PSK cipher suites
595                 // higher priority than X.509 cipher suites.
596                 // NOTE: There are cipher suites that use both X.509 and PSK (e.g., those based on
597                 // RSA_PSK key exchange). However, these cipher suites are not currently supported.
598                 return concat(
599                         NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
600                         NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
601                         new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
602             } else {
603                 // Only X.509 cipher suites need to be listed.
604                 return concat(
605                         NativeCrypto.DEFAULT_X509_CIPHER_SUITES,
606                         new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
607             }
608         } else if (pskCipherSuitesNeeded) {
609             // Only PSK cipher suites need to be listed.
610             return concat(
611                     NativeCrypto.DEFAULT_PSK_CIPHER_SUITES,
612                     new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV});
613         } else {
614             // Neither X.509 nor PSK cipher suites need to be listed.
615             return new String[] {NativeCrypto.TLS_EMPTY_RENEGOTIATION_INFO_SCSV};
616         }
617     }
618 
concat(String[].... arrays)619     private static String[] concat(String[]... arrays) {
620         int resultLength = 0;
621         for (String[] array : arrays) {
622             resultLength += array.length;
623         }
624         String[] result = new String[resultLength];
625         int resultOffset = 0;
626         for (String[] array : arrays) {
627             System.arraycopy(array, 0, result, resultOffset, array.length);
628             resultOffset += array.length;
629         }
630         return result;
631     }
632 
633     /**
634      * Check if SCT verification is enforced for a given hostname.
635      */
isCTVerificationEnabled(String hostname)636     boolean isCTVerificationEnabled(String hostname) {
637         if (hostname == null) {
638             return false;
639         }
640 
641         // Bypass the check. This is used for testing only
642         if (ctVerificationEnabled) {
643             return true;
644         }
645         return Platform.isCTVerificationRequired(hostname);
646     }
647 }
648