1package com.android.hotspot2.osu;
2
3import android.net.Network;
4import android.util.Base64;
5import android.util.Log;
6
7import com.android.hotspot2.Utils;
8import com.android.hotspot2.flow.PlatformAdapter;
9import com.android.hotspot2.pps.HomeSP;
10
11import java.io.ByteArrayInputStream;
12import java.io.IOException;
13import java.net.InetAddress;
14import java.net.InetSocketAddress;
15import java.net.Socket;
16import java.net.URL;
17import java.security.GeneralSecurityException;
18import java.security.KeyStore;
19import java.security.KeyStoreException;
20import java.security.PrivateKey;
21import java.security.cert.CertPath;
22import java.security.cert.CertPathValidator;
23import java.security.cert.CertPathValidatorException;
24import java.security.cert.Certificate;
25import java.security.cert.CertificateException;
26import java.security.cert.CertificateFactory;
27import java.security.cert.PKIXCertPathChecker;
28import java.security.cert.PKIXParameters;
29import java.security.cert.TrustAnchor;
30import java.security.cert.X509Certificate;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.Collection;
34import java.util.Collections;
35import java.util.HashSet;
36import java.util.List;
37import java.util.Map;
38import java.util.Set;
39
40import javax.net.SocketFactory;
41import javax.net.ssl.KeyManager;
42import javax.net.ssl.SSLContext;
43import javax.net.ssl.TrustManager;
44import javax.net.ssl.X509TrustManager;
45
46public class OSUSocketFactory {
47    private static final long ConnectionTimeout = 10000L;
48    private static final long ReconnectWait = 2000L;
49
50    private static final String SecureHTTP = "https";
51    private static final String UnsecureHTTP = "http";
52    private static final String EKU_ID = "2.5.29.37";
53    private static final Set<String> EKU_ID_SET = new HashSet<>(Arrays.asList(EKU_ID));
54    private static final EKUChecker sEKUChecker = new EKUChecker();
55
56    private final Network mNetwork;
57    private final SocketFactory mSocketFactory;
58    private final KeyManager mKeyManager;
59    private final WFATrustManager mTrustManager;
60    private final List<InetSocketAddress> mRemotes;
61
62    public static Set<X509Certificate> buildCertSet() {
63        try {
64            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
65            Set<X509Certificate> set = new HashSet<>();
66            for (String b64 : WFACerts) {
67                ByteArrayInputStream bis = new ByteArrayInputStream(
68                        Base64.decode(b64, Base64.DEFAULT));
69                X509Certificate cert = (X509Certificate) certFactory.generateCertificate(bis);
70                set.add(cert);
71            }
72            return set;
73        } catch (CertificateException ce) {
74            Log.e(OSUManager.TAG, "Cannot build CA cert set");
75            return null;
76        }
77    }
78
79    public static OSUSocketFactory getSocketFactory(KeyStore ks, HomeSP homeSP,
80                                                    OSUFlowManager.FlowType flowType,
81                                                    Network network, URL url, KeyManager km,
82                                                    boolean enforceSecurity)
83            throws GeneralSecurityException, IOException {
84
85        if (enforceSecurity && !url.getProtocol().equalsIgnoreCase(SecureHTTP)) {
86            throw new IOException("Protocol '" + url.getProtocol() + "' is not secure");
87        }
88        return new OSUSocketFactory(ks, homeSP, flowType, network, url, km);
89    }
90
91    private OSUSocketFactory(KeyStore ks, HomeSP homeSP, OSUFlowManager.FlowType flowType,
92                             Network network,
93                             URL url, KeyManager km) throws GeneralSecurityException, IOException {
94        mNetwork = network;
95        mKeyManager = km;
96        mTrustManager = new WFATrustManager(ks, homeSP, flowType);
97        int port;
98        switch (url.getProtocol()) {
99            case UnsecureHTTP:
100                mSocketFactory = new DefaultSocketFactory();
101                port = url.getPort() > 0 ? url.getPort() : 80;
102                break;
103            case SecureHTTP:
104                SSLContext tlsContext = SSLContext.getInstance("TLSv1");
105                tlsContext.init(km != null ? new KeyManager[]{km} : null,
106                        new TrustManager[]{mTrustManager}, null);
107                mSocketFactory = tlsContext.getSocketFactory();
108                port = url.getPort() > 0 ? url.getPort() : 443;
109                break;
110            default:
111                throw new IOException("Bad URL: " + url);
112        }
113        if (OSUManager.R2_MOCK && url.getHost().endsWith(".wi-fi.org")) {
114            // !!! Warning: Ruckus hack!
115            mRemotes = new ArrayList<>(1);
116            mRemotes.add(new InetSocketAddress(InetAddress.getByName("10.123.107.107"), port));
117        } else {
118            InetAddress[] remotes = mNetwork.getAllByName(url.getHost());
119            android.util.Log.d(OSUManager.TAG, "'" + url.getHost() + "' resolves to " +
120                    Arrays.toString(remotes));
121            if (remotes == null || remotes.length == 0) {
122                throw new IOException("Failed to look up host from " + url);
123            }
124            mRemotes = new ArrayList<>(remotes.length);
125            for (InetAddress remote : remotes) {
126                mRemotes.add(new InetSocketAddress(remote, port));
127            }
128        }
129        Collections.shuffle(mRemotes);
130    }
131
132    public void reloadKeys(Map<OSUCertType, List<X509Certificate>> certs, PrivateKey key)
133            throws IOException {
134        if (mKeyManager instanceof ClientKeyManager) {
135            ((ClientKeyManager) mKeyManager).reloadKeys(certs, key);
136        }
137    }
138
139    public Socket createSocket() throws IOException {
140        Socket socket = mSocketFactory.createSocket();
141        mNetwork.bindSocket(socket);
142
143        long bail = System.currentTimeMillis() + ConnectionTimeout;
144        boolean success = false;
145
146        while (System.currentTimeMillis() < bail) {
147            for (InetSocketAddress remote : mRemotes) {
148                try {
149                    socket.connect(remote);
150                    Log.d(OSUManager.TAG, "Connection " + socket.getLocalSocketAddress() +
151                            " to " + socket.getRemoteSocketAddress());
152                    success = true;
153                    break;
154                } catch (IOException ioe) {
155                    Log.d(OSUManager.TAG, "Failed to connect to " + remote + ": " + ioe);
156                    socket = mSocketFactory.createSocket();
157                    mNetwork.bindSocket(socket);
158                }
159            }
160            if (success) {
161                break;
162            }
163            Utils.delay(ReconnectWait);
164        }
165        if (!success) {
166            throw new IOException("No available network");
167        }
168        return socket;
169    }
170
171    public X509Certificate getOSUCertificate(URL url) throws GeneralSecurityException {
172        String fqdn = url.getHost();
173        for (X509Certificate certificate : mTrustManager.getTrustChain()) {
174            for (List<?> name : certificate.getSubjectAlternativeNames()) {
175                if (name.size() >= SPVerifier.DNSName &&
176                        name.get(0).getClass() == Integer.class &&
177                        name.get(1).toString().equals(fqdn)) {
178                    return certificate;
179                }
180            }
181        }
182        return null;
183    }
184
185    final class DefaultSocketFactory extends SocketFactory {
186
187        DefaultSocketFactory() {
188        }
189
190        @Override
191        public Socket createSocket() throws IOException {
192            return new Socket();
193        }
194
195        @Override
196        public Socket createSocket(String host, int port) throws IOException {
197            return new Socket(host, port);
198        }
199
200        @Override
201        public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
202                throws IOException {
203            return new Socket(host, port, localHost, localPort);
204        }
205
206        @Override
207        public Socket createSocket(InetAddress host, int port) throws IOException {
208            return new Socket(host, port);
209        }
210
211        @Override
212        public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
213                                   int localPort) throws IOException {
214            return new Socket(address, port, localAddress, localPort);
215        }
216    }
217
218    private static class WFATrustManager implements X509TrustManager {
219        private final KeyStore mKeyStore;
220        private final HomeSP mHomeSP;
221        private final OSUFlowManager.FlowType mFlowType;
222        private X509Certificate[] mTrustChain;
223
224        private WFATrustManager(KeyStore ks, HomeSP homeSP, OSUFlowManager.FlowType flowType)
225                throws CertificateException {
226            mKeyStore = ks;
227            mHomeSP = homeSP;
228            mFlowType = flowType;
229        }
230
231        @Override
232        public void checkClientTrusted(X509Certificate[] chain, String authType)
233                throws CertificateException {
234            // N/A
235        }
236
237        @Override
238        public void checkServerTrusted(X509Certificate[] chain, String authType)
239                throws CertificateException {
240            Log.d("TLSOSU", "Checking " + chain.length + " certs.");
241
242            try {
243                CertPathValidator validator =
244                        CertPathValidator.getInstance(CertPathValidator.getDefaultType());
245                CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
246                CertPath path = certFactory.generateCertPath(
247                        Arrays.asList(chain));
248                Set<TrustAnchor> trustAnchors = new HashSet<>();
249                if (mHomeSP == null) {
250                    for (X509Certificate cert : getRootCerts(mKeyStore)) {
251                        trustAnchors.add(new TrustAnchor(cert, null));
252                    }
253                } else {
254                    String prefix = mFlowType == OSUFlowManager.FlowType.Remediation ?
255                            PlatformAdapter.CERT_REM_ALIAS : PlatformAdapter.CERT_POLICY_ALIAS;
256
257                    X509Certificate cert = getCert(mKeyStore, prefix + mHomeSP.getFQDN());
258                    if (cert == null) {
259                        cert = getCert(mKeyStore,
260                                PlatformAdapter.CERT_SHARED_ALIAS + mHomeSP.getFQDN());
261                    }
262                    if (cert == null) {
263                        for (X509Certificate root : getRootCerts(mKeyStore)) {
264                            trustAnchors.add(new TrustAnchor(root, null));
265                        }
266                    } else {
267                        trustAnchors.add(new TrustAnchor(cert, null));
268                    }
269                }
270                PKIXParameters params = new PKIXParameters(trustAnchors);
271                params.setRevocationEnabled(false);
272                params.addCertPathChecker(sEKUChecker);
273                validator.validate(path, params);
274                mTrustChain = chain;
275            } catch (GeneralSecurityException gse) {
276                throw new SecurityException(gse);
277            }
278            mTrustChain = chain;
279        }
280
281        @Override
282        public X509Certificate[] getAcceptedIssuers() {
283            return null;
284        }
285
286        public X509Certificate[] getTrustChain() {
287            return mTrustChain != null ? mTrustChain : new X509Certificate[0];
288        }
289    }
290
291    private static X509Certificate getCert(KeyStore keyStore, String alias)
292            throws KeyStoreException {
293        Certificate cert = keyStore.getCertificate(alias);
294        if (cert != null && cert instanceof X509Certificate) {
295            return (X509Certificate) cert;
296        }
297        return null;
298    }
299
300    public static Set<X509Certificate> getRootCerts(KeyStore keyStore) throws KeyStoreException {
301        Set<X509Certificate> certSet = new HashSet<>();
302        int index = 0;
303        for (int n = 0; n < 1000; n++) {
304            Certificate cert = keyStore.getCertificate(
305                    String.format("%s%d", PlatformAdapter.CERT_WFA_ALIAS, index));
306            if (cert == null) {
307                break;
308            } else if (cert instanceof X509Certificate) {
309                certSet.add((X509Certificate) cert);
310            }
311            index++;
312        }
313        return certSet;
314    }
315
316    private static class EKUChecker extends PKIXCertPathChecker {
317        @Override
318        public void init(boolean forward) throws CertPathValidatorException {
319
320        }
321
322        @Override
323        public boolean isForwardCheckingSupported() {
324            return true;
325        }
326
327        @Override
328        public Set<String> getSupportedExtensions() {
329            return EKU_ID_SET;
330        }
331
332        @Override
333        public void check(Certificate cert, Collection<String> unresolvedCritExts)
334                throws CertPathValidatorException {
335            Log.d(OSUManager.TAG, "Checking EKU " + unresolvedCritExts);
336            unresolvedCritExts.remove(EKU_ID);
337        }
338    }
339
340    /*
341     *
342      Subject: CN=osu-server.r2-testbed-rks.wi-fi.org, O=Intel Corporation CCG DRD, C=US
343      Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
344      Validity: [From: Wed Jan 28 16:00:00 PST 2015,
345                   To: Sat Jan 28 15:59:59 PST 2017]
346      Issuer: CN="NetworkFX, Inc. Hotspot 2.0 Intermediate CA", OU=OSU CA - 01, O="NetworkFX, Inc.", C=US
347      SerialNumber: [    312af3db 138eae19 1defbce2 e2b88b55]
348    *
349    *
350      Subject: CN="NetworkFX, Inc. Hotspot 2.0 Intermediate CA", OU=OSU CA - 01, O="NetworkFX, Inc.", C=US
351      Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11
352      Validity: [From: Tue Nov 19 16:00:00 PST 2013,
353                   To: Sun Nov 19 15:59:59 PST 2023]
354      Issuer: CN=Hotspot 2.0 Trust Root CA - 01, O=WFA Hotspot 2.0, C=US
355      SerialNumber: [    4152b1b0 301495f3 8fa76428 2ef41046]
356     */
357
358    public static final String[] WFACerts = {
359            "MIIFbDCCA1SgAwIBAgIQDLMPcPKGpDPguQmJ3gHttzANBgkqhkiG9w0BAQsFADBQ" +
360                    "MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPV0ZBIEhvdHNwb3QgMi4wMScwJQYDVQQD" +
361                    "Ex5Ib3RzcG90IDIuMCBUcnVzdCBSb290IENBIC0gMDMwHhcNMTMxMjA4MTIwMDAw" +
362                    "WhcNNDMxMjA4MTIwMDAwWjBQMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPV0ZBIEhv" +
363                    "dHNwb3QgMi4wMScwJQYDVQQDEx5Ib3RzcG90IDIuMCBUcnVzdCBSb290IENBIC0g" +
364                    "MDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsdEtReIUbMlO+hR6b" +
365                    "yQk4nGVITv3meYTaDeVwZnQVal8EjHuu4Kd89g8yRYVTv3J1kq9ukE7CDrDehrXK" +
366                    "ym+8VlR7ro0lB/lwRyNk3W7yNccg3AknQ0x5fKVwcFznwD/FYg37owGmhGFtpMTB" +
367                    "cxzreQaLXvLta8YNlJU10ZkfputBpzi9bLPWsLOkIrQw7KH1Wc+Oiy4hUMUbTlSi" +
368                    "cjqacKPR188mVIoxxUoICHyVV1KvMmYZrVdc/b5dbmd0haMHxC0VSqbydXxxS7vv" +
369                    "/lCrC2d5qbKE66PiuBPkhzyU7SI9C8GU/S7akYm1MMSTn5W7lSp2AWRDnf9LQg51" +
370                    "dLvDxJ7t2fruXtSkkqG/cwY1yQI8O+WZYPDThKPcDmNbaxVE9lOizAHXFVsfYrXA" +
371                    "PbbMOkzKehYwaIikmNgcpxtQNw+wikJiZb9N8VwwtwHK71XEFi+n5DGlPa9VDYgB" +
372                    "YkBcxvVo2rbE3i3teQgHm+pWZNP08aFNWwMk9yQkm/SOGdLq1jLbQA9yd7fyR1Ct" +
373                    "W1GLzKi1Ojr/6XiB9/noL3oxP/+gb8OSgcqVfkZp4QLvrGdlKiOI2fE7Bslmzn6l" +
374                    "B3UTpApjab7BQ99rCXzDwt3Xd7IrCtAJNkxi302J7k6hnGlW8S4oPQBElkOtoH9y" +
375                    "XEhp9rNS0lZiuwtFmWW2q50fkQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G" +
376                    "A1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUZw5JLGEXnuvt4FTnhNmbrWRgc2UwDQYJ" +
377                    "KoZIhvcNAQELBQADggIBAFPoGFDyzFg9B9+jJUPGW32omftBhChVcgjllI07RCie" +
378                    "KTMBi47+auuLgiMox3xRyP7/dX7YaUeMXEQ1BMv6nlrsXWv1lH4yu+RNuehPlqRs" +
379                    "fY351mAfPtQ654SBUi0Wg++9iyTOfgF5a9IWEDt4lnSZMvA4vlw8pUCz6zpKXHnA" +
380                    "RXKrpY3bU+2dnrFDKR0XQhmAQdo7UvdsT1elVoFIxHhLpwfzx+kpEhtrXw3nGgt+" +
381                    "M4jNp684XoWpxVGaQ4Vvv00Sm2DQ8jq2sf9F+kRWszZpQOTiMGKZr0lX2CI5cww1" +
382                    "dfmd1BkAjI9cIWLkD8YSeaggZzvYe1o9d7e7lKfdJmjDlSQ0uBiG77keUK4tF2fi" +
383                    "xFTxibtPux56p3GYQ2GdRsBaKjH3A3HMJSKXwIGR+wb1sgz/bBdlyJSylG8hYD//" +
384                    "0Hyo+UrMUszAdszoPhMY+4Ol3QE3QRWzXi+W/NtKeYD2K8xUzjZM10wMdxCfoFOa" +
385                    "8bzzWnxZQlnu880ULUSHIxDPeE+DDZYYOaN1hV2Rh/hrFKvvV+gJj2eXHF5G7y9u" +
386                    "Yg7nHYCCf7Hy8UTIXDtAAeDCQNon1ReN8G+XOqhLQ9TalmnJ5U5ARtC0MdQDht7T" +
387                    "DZpWeEVv+pQHARX9GDV/T85MV2RPJWKqfZ6kK0gvQDkunADdg8IhZAjwMMx3k6B/",
388
389            "MIIFbDCCA1SgAwIBAgIQaAV8NQv/Xdusi4IU+tpUfjANBgkqhkiG9w0BAQsFADBQ" +
390                    "MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPV0ZBIEhvdHNwb3QgMi4wMScwJQYDVQQD" +
391                    "Ex5Ib3RzcG90IDIuMCBUcnVzdCBSb290IENBIC0gMDEwHhcNMTMxMTIwMDAwMDAw" +
392                    "WhcNNDMxMTE5MjM1OTU5WjBQMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPV0ZBIEhv" +
393                    "dHNwb3QgMi4wMScwJQYDVQQDEx5Ib3RzcG90IDIuMCBUcnVzdCBSb290IENBIC0g" +
394                    "MDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/gf4CHxWjr2EcktAZ" +
395                    "pHT4z1yFYZILD3ZVqvzzXBK+YKjWhjsgZ28Z1VwXqu51JvVzwTGDalPf5m7zMcJW" +
396                    "CpPtPBdxxwQ/cBDPK4w+/sCuYYSddlMLzwZ/IgwFike12tKTR7Kk7Nk6ghrYaxCG" +
397                    "R+QEZDVrxITj79vGpgk2otVnMI4d3H9mWt1o6Lx+hVioyBgOvmo2OWHR2uKkbg5h" +
398                    "tktXqmBEtzK+qDqIIUY4WRRZHxlOaF2/EdIIGhXlf+Vlr13aPqOPiDiE08o+GARz" +
399                    "TIp8BrW2boo0+2kpEFUKiqc427vOYEkUdSMfwu4aGOcuOewc8sk6ztquL/JcPROL" +
400                    "VSFSSFR3HKhUto8EJcHEEG9wzcOi1OO/OOSVxjNwiaV/hB9Ed1wvoBhiJ+C+Q8/K" +
401                    "HXmoH/ankXDaB06yjt2Ojemt0nO45qlarRj8tO7zbpghJuJxztur47U7PJta7Zcg" +
402                    "z7kOPJPTAbzmOU2TXt1pXO1hVnSlV+M1rRwe7qivnSMMrTnkX15YWmyK27/tgJeu" +
403                    "muR2YzvPwPtF/m1N0bRKI7FW05NYg3smItFq0E/eyf/orgolcXTZ7zNRyRGnjWNs" +
404                    "/w9SDbdby0uVUfdN4V/5uC4HBmA1rikoBbGZ+nzCtesY4yW8eEwMfguVpNT3ueaU" +
405                    "q30nufeY2VnA3Rv1WH8TaeZU+wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G" +
406                    "A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU+RjGVZbebjpzEPfthaTLqbvXMiEwDQYJ" +
407                    "KoZIhvcNAQELBQADggIBABj3LP1UXVa16HYeXC1+GU1dX/cla1n1bwpIlxRnCZ5/" +
408                    "3I3zGw/nRnsLUTkGf8q3XCgin+jX22kyzzQNrgepn0zqBsmAj+pjUUwWzYQUzphc" +
409                    "Uzmg4PJRWaEaGG3kvD+wJEC0pWvIhe48qcq8FZCCmjbvecEVn5mM0smPzPyUjf/o" +
410                    "fjUMQvVWqug/Ff5HT6kbyDWhC3nD+8IZ5PjyO85OnoBnQkr8WYwr24XJgO2HS2rs" +
411                    "W40CzQe3Kdg7HHyef+/iyLYTBJH7EUJPCHGVQtZ3q0aNqURkutXJ/CxKJYMcNTEB" +
412                    "x+a09EhZ6DOHQDqsdTuAqGh3VyrxhFk+3suNsxoh6XaRK10VslvdNB/1YKfU8DWe" +
413                    "V6XfDH/TR0NIL04exUp3rER8sERulpJGBOnaG6OQKh4bFYDB406+QfusQnvO0aYR" +
414                    "UXJzf01B15HRJgpZsggpIuex0UDcJhTTpkRfTj8L4ayUce2ZRsGn3dBaT9ZMx4o9" +
415                    "E/YsQyOpfw28gM5u+zZt4BJz4gAaRGbp4r4sk5Vm/P1/0EXJ70Du6K9d0HAHtpEv" +
416                    "Y94Ww5W6fpMDdyAKYTXZBgTX3cqtikNkLX/kHH8l4o/XW2sXqU3X7vOYqgeVYoD9" +
417                    "NnhZXYCerH4Se5Lgj8/KhXxRWtcn3XduMdkC6UTApMooA64Vs508173Z3lJn2SeQ",
418
419            "MIIFXTCCA0WgAwIBAgIBATANBgkqhkiG9w0BAQsFADBQMQswCQYDVQQGEwJVUzEY" +
420                    "MBYGA1UECgwPV0ZBIEhvdHNwb3QgMi4wMScwJQYDVQQDDB5Ib3RzcG90IDIuMCBU" +
421                    "cnVzdCBSb290IENBIC0gMDIwHhcNMTMxMjAyMjA1NzU3WhcNNDMxMjAyMjA1NTAz" +
422                    "WjBQMQswCQYDVQQGEwJVUzEYMBYGA1UECgwPV0ZBIEhvdHNwb3QgMi4wMScwJQYD" +
423                    "VQQDDB5Ib3RzcG90IDIuMCBUcnVzdCBSb290IENBIC0gMDIwggIiMA0GCSqGSIb3" +
424                    "DQEBAQUAA4ICDwAwggIKAoICAQDCSoMqNhtTwbnIsINp6nUhx5UFuq9ZQoTv+KDk" +
425                    "vAajT0di6+cQG3sAVvZLySmJoiBAv3PizYYLOD4eGMrFQRqi7PmSJ83WqNv23ZYF" +
426                    "ryFFJiy/URXc/ALDuB3dgElPt24Mx7n2xDPAh9t82HTmuskpQRrsyg9QPoi5rRRS" +
427                    "Djm5mjFJjKChq99RWcweNV/KGH1sTwcmlDmNMScK16A+BBNiSvmZlsGJgAlP369k" +
428                    "lnNqt6UiDhepcktuKpHmSvNel+c/xqzR0gURfUnXcZhzjzS94Rx5O+CNWL4EGiJq" +
429                    "qKAfk99j/lbD0MWYo7Rh0UKQlXSdohWDiV93hxvvfugej8KUOIb+1wmd1Fi+lwDZ" +
430                    "bR2yg2f0qyxbC/tAV4JJNnuDLFb19leD78x+68eAnlbMi+xMH5lINs15+26s2H5d" +
431                    "lx9kwRDBJq02LuHnen6FLafWjejnnBQ/PuGD0ACvBegSsDKDaCuTAnTNS6MDmQr4" +
432                    "wza08iX360ZN+BbSAnCK1YGa/7J7fhyydwxLJ7s5Eo0b6SUMY87FMc5XmkAk4xxL" +
433                    "MLqS2HMtqsGBI5JQT0SgH0ghE6DjMWArBTZcD+swuzTi1/Cz5+Z9Es8xJ3MPvSZW" +
434                    "pJi6VVB2eVMAqfHOj4ozHoVpvJypIVGRwWBzVRWom76R47utuRK6uKzoLiB1jwE5" +
435                    "vwHpUQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBxjAd" +
436                    "BgNVHQ4EFgQU5C9c1OMsB+/MOwl9OKG2D/XSwrUwDQYJKoZIhvcNAQELBQADggIB" +
437                    "AGULYE/VrnA3K0ptgHrWlQoPfp5wGvScgsmy0wp9qE3b6n/4bLehBKb5w4Y3JVA9" +
438                    "gjxoQ5xE2ssDtULZ3nKnGWmMN3qOBoRZCA6KjKs1860p09tm1ScUsajDJ15Tp1nI" +
439                    "zfR0oP63+2bJx+JXM8fPKOJe245hj2rs1c3JXsGCe+UVrlGsotG+wR0PdrejaXJ8" +
440                    "HbhBQHcbhgjsD1Gb6Egm4YxRKAtcVY3q9EKKWAGhbC1qvCh1iLNKo3FeGgm2r3EG" +
441                    "L4cYJBb2fhSKltjISqCDhYq4tplOIeQSJJyJC8gfW/BnMU39lTjNgnSjjGPLQXGV" +
442                    "+Ulb/CgNMJ3RhRJdBoLcpIm/EeLx6JLq/2Erxy7CxjaSOcD0UKa14+dzLSHVsXft" +
443                    "HZuOy548X8m18KruSZsf5uAT3c7NqlXtr9YgOVUqSJykNAHTGi/BHB1dC2clKvxN" +
444                    "ElfLWWrG9yaAd5TFW0+3wsaDIwRZL584AsFwwAD3KMo1oU/2zRvtm0E+VghsuD/Z" +
445                    "IE1xaVGTPaL7ph/YgC9+0rGHieauT8SXz6Ryp3h0RtYMLFZOMTKM7xjmcbMZDwrO" +
446                    "c+J/XjK9dbiCqlx5/B8P0xWaYYHzvE5/fafiPYzoGyFVUXquu0dFCCQrvjF/y0tC" +
447                    "TPm4hQim3k1F+5NChcbeNggN+kq+VdlSqPhQEuOY+kNv"
448    };
449
450    //private static final Set<TrustAnchor> sTrustAnchors = buildCertSet();
451}
452