2002/05/05 08:37:32
[org.ibex.core.git] / src / org / xwt / TinySSL.java
1 // Copyright (C) 2001 Adam Megacz <adam@xwt.org> all rights reserved.
2 //
3 // You may modify, copy, and redistribute this code under the terms of
4 // the GNU Library Public License version 2.1, with the exception of
5 // the portion of clause 6a after the semicolon (aka the "obnoxious
6 // relink clause")
7
8 package org.xwt;
9
10 import org.bouncycastle.crypto.AsymmetricBlockCipher;
11 import org.bouncycastle.crypto.Digest;
12 import org.bouncycastle.crypto.CipherParameters;
13 import org.bouncycastle.crypto.InvalidCipherTextException;
14 import org.bouncycastle.crypto.params.RSAKeyParameters;
15 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
16 import org.bouncycastle.crypto.params.KeyParameter;
17 import org.bouncycastle.crypto.digests.SHA1Digest;
18 import org.bouncycastle.crypto.digests.MD5Digest;
19 import org.bouncycastle.crypto.digests.MD2Digest;
20 import org.bouncycastle.crypto.engines.RSAEngine;
21 import org.bouncycastle.crypto.engines.RC4Engine;
22 import org.bouncycastle.util.encoders.Base64;
23 import org.bouncycastle.asn1.DERInputStream;
24 import org.bouncycastle.asn1.DEROutputStream;
25 import org.bouncycastle.asn1.DERConstructedSequence;
26 import org.bouncycastle.asn1.DERObject;
27 import org.bouncycastle.asn1.DEROctetString;
28 import org.bouncycastle.asn1.BERInputStream;
29 import org.bouncycastle.asn1.x509.X509CertificateStructure;
30 import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
31 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
32 import org.bouncycastle.asn1.x509.TBSCertificateStructure;
33 import org.bouncycastle.asn1.x509.X509Name;
34 import org.xwt.util.Log;
35 import java.net.*;
36 import java.io.*;
37 import java.util.*;
38 import java.math.*;
39 import java.text.*;
40
41 /**
42
43    TinySSL: a tiny SSL implementation in Java, built on the
44             bouncycastle.org lightweight crypto library.
45
46    This class implements an SSLv3 client-side socket, with the
47    SSL_RSA_EXPORT_WITH_RC4_40_MD5 and SSL_RSA_WITH_RC4_128_MD5 cipher
48    suites, as well as certificate chain verification against a
49    collection of 93 built-in Trusted Root CA public keys (the same 93
50    included with Microsoft Internet Explorer 5.5 SP2).
51
52    As of 07-Dec-01, the zipped bytecode for this class is 43k, and the
53    subset of bouncycastle it requires is 82k.
54
55    This class should work correctly on any Java 1.1 compliant
56    platform. The java.security.* classes are not used.
57
58    The main design goal for this class was the smallest possible body
59    of code capable of connecting to 99% of all active HTTPS
60    servers. Although this class is useful in many other situations
61    (IMAPS, Secure SMTP, etc), the author will refuse all feature
62    requests and submitted patches which go beyond this scope.
63
64    Because of the limited goals of this class, certain abstractions
65    have been avoided, and certain parameters have been
66    hard-coded. "Magic numbers" are often used instead of "static final
67    int"'s, although they are usually accompanied by a descriptive
68    comment. Numeric offsets into byte arrays are also favored over
69    DataInputStream(ByteArrayInputStream(foo))'s.
70
71    Much thanks and credit go to the BouncyCastle team for producing
72    such a first-class library, and for helping me out on the
73    dev-crypto mailing list while I was writing this.
74
75    Revision History:
76
77    1.0  07-Dec-01  Initial Release
78
79    1.01 15-Mar-02  Added PKCS1 class to avoid dependancy on java.security.SecureRandom
80
81    1.02 27-Mar-02  Fixed a bug which would hang the connection when more than one
82                    Handshake message appeared in the same TLS Record
83
84 */
85
86 public class TinySSL extends Socket {
87
88     // Simple Test //////////////////////////////////////////////
89
90     public static void main(String[] args) {
91         Log.on = true;
92         try {
93             Socket s = new TinySSL("www.verisign.com", 443);
94             PrintWriter pw = new PrintWriter(s.getOutputStream());
95             BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
96             pw.println("GET / HTTP/1.0");
97             pw.println("");
98             pw.flush();
99             
100             while(true) {
101                 String s2 = br.readLine();
102                 if (s2 == null) return;
103                 System.out.println(s2);
104             }
105             
106         } catch (Exception e) {
107             e.printStackTrace();
108         }
109     }
110
111     // Static Data //////////////////////////////////////////////
112
113     public static class SSLException extends IOException { public SSLException(String s) { super(s); } }
114     static SubjectPublicKeyInfo[] trusted_CA_public_keys;
115     public static byte[] pad1 = new byte[48];
116     public static byte[] pad2 = new byte[48];
117     public static byte[] pad1_sha = new byte[40];
118     public static byte[] pad2_sha = new byte[40];
119     static byte[] randpool;
120     static long randcnt = 0;
121
122     // Cipher State //////////////////////////////////////////////
123
124     public byte[] server_random = new byte[32];
125     public byte[] client_random = new byte[32];
126     public byte[] client_write_MAC_secret = new byte[16];        
127     public byte[] server_write_MAC_secret = new byte[16];        
128     public byte[] client_write_key = null;
129     public byte[] server_write_key = null;
130     public byte[] master_secret = null;
131
132     /** the bytes of the ServerKeyExchangeMessage, null if none recieved */
133     public byte[] serverKeyExchange = null;
134
135     /** true iff the server asked for a certificate */
136     public boolean cert_requested = false;
137
138     public X509CertificateStructure server_cert = null;
139
140     public SSLOutputStream os;
141     public SSLInputStream is;
142
143     String hostname;
144
145     /** the concatenation of all the bytes of all handshake messages sent or recieved */
146     public byte[] handshakes = new byte[] { };
147
148     /** true iff we're using SSL_RSA_EXPORT_WITH_RC4_40_MD5 */
149     boolean export = false;
150
151     public InputStream getInputStream() { return is; }
152     public OutputStream getOutputStream() { return os; }
153
154     public TinySSL(String host, int port) throws IOException { this(host, port, true); }
155     public TinySSL(String host, int port, boolean negotiateImmediately) throws IOException {
156         super(host, port);
157         hostname = host;
158         if (negotiateImmediately) negotiate();
159     }
160
161     /** negotiates the SSL connection */
162     public void negotiate() throws IOException {
163         os = new SSLOutputStream(super.getOutputStream());
164         is = new SSLInputStream(super.getInputStream());
165         os.writeClientHello();
166         is.readServerHandshakes();
167         os.sendClientHandshakes();
168         is.readServerFinished();
169     }
170
171     class SSLInputStream extends InputStream {
172         
173         /** the underlying inputstream */
174         DataInputStream raw;
175
176         /** the server's sequence number */
177         public int seq_num = 0;
178
179         /** the decryption engine */
180         public RC4Engine rc4 = null;
181         
182         /** pending bytes -- decrypted, but not yet fed to consumer */
183         byte[] pend = null;
184         int pendstart = 0;
185         int pendlen = 0;
186
187         public void mark() { }
188         public void reset() { }
189         public boolean markSupported() { return false; }
190         public long skip(long l) throws IOException { for(long i=0; i<l; i++) read(); return l; }
191         public SSLInputStream(InputStream raw) { this.raw = new DataInputStream(raw); }
192         public int available() throws IOException { return pendlen; }
193
194         public int read() throws IOException {
195             byte[] singlebyte = new byte[1];
196             int numread = read(singlebyte);
197             if (numread != 1) return -1;
198             return (int)singlebyte[0];
199         }
200        
201         public int read(byte[] b, int off, int len) throws IOException {
202             if (pendlen == 0) {
203                 pend = readRecord();
204                 if (pend == null) return -1;
205                 pendstart = 0;
206                 pendlen = pend.length;
207             }
208             int ret = Math.min(len, pendlen);
209             System.arraycopy(pend, pendstart, b, off, ret);
210             pendlen -= ret;
211             pendstart += ret;
212             return ret;
213         }
214
215         /** reads and decrypts exactly one record; blocks if unavailable */        
216         public byte[] readRecord() throws IOException {
217
218             // we only catch EOFException here, because anywhere else
219             // would be "unusual", and we *want* and EOFException in
220             // those cases
221             byte type;
222             try { type = raw.readByte();
223             } catch (EOFException e) {
224                 if (Log.on) Log.log(this, "got EOFException reading packet type");
225                 return null;
226             }
227
228             byte ver_major = raw.readByte();
229             byte ver_minor = raw.readByte();
230             short len = raw.readShort();
231             if (Log.on) Log.log(this, "got record of type " + type + ", SSLv" + ver_major + "." + ver_minor + ", length=" + len);
232
233             byte[] ret = new byte[len];
234             raw.readFully(ret);
235             
236             // simply ignore ChangeCipherSpec messages -- we change as soon as we send ours
237             if (type == 20) {
238                 if (Log.on) Log.log(this, "got ChangeCipherSpec; ignoring");
239                 seq_num = 0;
240                 return readRecord();
241             }
242
243             byte[] decrypted_payload;
244
245             // if crypto hasn't been enabled yet; skip crypt and hash
246             if (rc4 == null) decrypted_payload = ret;
247             else {
248                 // decrypt the payload
249                 decrypted_payload = new byte[len - 16];
250                 rc4.processBytes(ret, 0, len - 16, decrypted_payload, 0);
251                 
252                 // check the MAC
253                 byte[] MAC = new byte[16];
254                 rc4.processBytes(ret, len - 16, 16, MAC, 0);
255                 byte[] ourMAC = computeMAC(type, decrypted_payload, 0, decrypted_payload.length, server_write_MAC_secret, seq_num++);
256                 for(int i=0; i<MAC.length; i++)
257                     if (MAC[i] != ourMAC[i])
258                         throw new SSLException("MAC mismatch on byte " + i + ": got " + MAC[i] + ", expecting " + ourMAC[i]);
259             }
260
261             if (type == 21) {
262                 if (decrypted_payload[1] > 1) {
263                     throw new SSLException("got SSL ALERT message, level=" + decrypted_payload[0] + " code=" + decrypted_payload[1]);
264                 } else if (decrypted_payload[1] == 0) {
265                     if (Log.on) Log.log(this, "server requested connection closure; returning null");
266                     return null;
267                 } else {
268                     if (Log.on) Log.log(this, "got SSL ALERT message, level=" + decrypted_payload[0] + " code=" + decrypted_payload[1]);
269                     return readRecord();
270                 }
271
272             } else if (type == 22) {
273                 if (Log.on) Log.log(this, "read a handshake");
274
275             } else if (type != 23) {
276                 if (Log.on) Log.log(this, "unexpected record type: " + type + "; skipping");
277                 return readRecord();
278
279             }
280                 
281             if (Log.on) Log.log(this, "  returning " + decrypted_payload.length + " byte record payload");
282             return decrypted_payload;
283         }
284
285         private byte[] readHandshake() throws IOException {
286             // acquire a handshake message
287             byte type = (byte)read();
288             int len = ((read() & 0xff) << 16) | ((read() & 0xff) << 8) | (read() & 0xff);
289             byte[] rec = new byte[len + 4];
290             rec[0] = type;
291             rec[1] = (byte)(((len & 0x00ff0000) >> 16) & 0xff);
292             rec[2] = (byte)(((len & 0x0000ff00) >> 8) & 0xff);
293             rec[3] = (byte)((len & 0x000000ff) & 0xff);
294             if (len > 0) read(rec, 4, len);
295             return rec;
296         }
297
298         /** This reads the ServerHello, Certificate, and ServerHelloDone handshake messages */
299         public void readServerHandshakes() throws IOException {
300             for(;;) {
301
302                 byte[] rec = readHandshake();
303                 handshakes = concat(new byte[][] { handshakes, rec });
304                 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(rec, 4, rec.length - 4));
305
306                 switch(rec[0]) {
307                 case 2: // ServerHello
308                     if (Log.on) Log.log(this, "got ServerHello");
309                     byte ver_major = rec[4];
310                     byte ver_minor = rec[5];
311                     System.arraycopy(rec, 6, server_random, 0, server_random.length);
312                     short cipher_high = rec[6 + server_random.length + rec[6 + server_random.length] + 1];
313                     short cipher_low = rec[6 + server_random.length + rec[6 + server_random.length] + 2];
314
315                     if (cipher_low == 0x04 || cipher_high != 0x00) {
316                         export = false;
317                         if (Log.on) Log.log(this, "using SSL_RSA_WITH_RC4_128_MD5");
318
319                     } else if (cipher_low == 0x03 || cipher_high != 0x00) {
320                         export = true;
321                         if (Log.on) Log.log(this, "using SSL_RSA_EXPORT_WITH_RC4_40_MD5");
322
323                     } else throw new SSLException("server asked for cipher " + ((cipher_high << 8) | cipher_low) +
324                                                 " but we only do SSL_RSA_WITH_RC4_128_MD5 (0x0004) and " +
325                                                 "SSL_RSA_EXPORT_WITH_RC4_40_MD5 (0x0003)");
326
327                     byte compressionMethod = rec[6 + server_random.length + rec[6 + server_random.length] + 3];
328                     if (compressionMethod != 0x0) throw new SSLException("server asked for compression method " + compressionMethod +
329                                                                          " but we don't support compression");
330                     break;
331                     
332                 case 11: // Server's certificate(s)
333                     if (Log.on) Log.log(this, "got Server Certificate(s)");
334                     int numcertbytes = ((rec[4] & 0xff) << 16) | ((rec[5] & 0xff) << 8) | (rec[6] & 0xff);
335                     int numcerts = 0;
336                     X509CertificateStructure last_cert = null;
337                     X509CertificateStructure this_cert = null;
338
339                     for(int i=0; i<numcertbytes;) {
340                         int certlen = ((rec[7 + i] & 0xff) << 16) | ((rec[7 + i + 1] & 0xff) << 8) | (rec[7 + i + 2] & 0xff);
341                         try {
342                             DERInputStream dIn = new DERInputStream(new ByteArrayInputStream(rec, 7 + i + 3, certlen));
343                             this_cert = new X509CertificateStructure((DERConstructedSequence)dIn.readObject());
344                         } catch (Exception e) {
345                             SSLException t = new SSLException("error decoding server certificate: " + e);
346                             t.fillInStackTrace();
347                             throw t;
348                         }
349
350                         if (server_cert == null) {
351                             server_cert = this_cert;
352                             TBSCertificateStructure tbs = server_cert.getTBSCertificate();
353                             X509Name subject = tbs.getSubject();
354
355                             // gross hack to extract the Common Name so we can compare it to the server hostname
356                             String CN = tbs.getSubject().toString() + " ";
357                             boolean good = false;
358                             for(int j=0; j<CN.length() - 3; j++)
359                                 if (CN.substring(j, j+3).equals("CN=")) {
360                                     good = true;
361                                     CN = CN.substring(j+3, CN.indexOf(' ', j+3));
362                                     break;
363                                 }
364
365                             if (!good) throw new SSLException("server certificate does not seem to have a CN: " + CN);
366                             if (!CN.equals(hostname))
367                                 throw new SSLException("connecting to host " + hostname + " but server certificate was issued for " + CN);
368
369                             SimpleDateFormat dateF = new SimpleDateFormat("MM-dd-yy-HH-mm-ss-z");
370
371                             // the following idiocy is a result of the brokenness of the GNU Classpath's SimpleDateFormat
372                             String s = tbs.getStartDate().getTime();
373                             s = s.substring(2, 4) + "-" + s.substring(4, 6) + "-" + s.substring(0, 2) + "-" + s.substring(6, 8) + "-" +
374                                 s.substring(8, 10) + "-" + s.substring(10, 12) + "-" + s.substring(12);
375                             Date startDate = dateF.parse(s, new ParsePosition(0));
376
377                             s = tbs.getEndDate().getTime();
378                             s = s.substring(2, 4) + "-" + s.substring(4, 6) + "-" + s.substring(0, 2) + "-" + s.substring(6, 8) + "-" +
379                                 s.substring(8, 10) + "-" + s.substring(10, 12) + "-" + s.substring(12);
380                             Date endDate = dateF.parse(s, new ParsePosition(0));
381
382                             Date now = new Date();
383                             if (now.after(endDate)) throw new SSLException("server certificate expired on " + endDate);
384                             if (now.before(startDate)) throw new SSLException("server certificate will not be valid until " + startDate);
385
386                             Log.log(this, "server cert (name, validity dates) checks out okay");
387                             
388                         } else if (!isSignedBy(last_cert, this_cert.getSubjectPublicKeyInfo()))
389                             throw new SSLException("certificate chain discontinuity");
390
391                         last_cert = this_cert;
392                         i += certlen + 3;
393                         numcerts++;
394                     }
395                     if (Log.on) Log.log(this, "  Certificate (" + numcerts + " certificates)");
396
397                     boolean good = false;
398                     for(int i=0; i<trusted_CA_public_keys.length; i++) {
399                         if (isSignedBy(this_cert, trusted_CA_public_keys[i])) {
400                             if (Log.on) Log.log(this, "server cert was signed by trusted CA " + i);
401                             good = true;
402                             break;
403                         }
404                     }
405                     if (!good) throw new SSLException("server cert was not signed by a trusted CA");
406                     break;
407
408                 case 12: 
409                     if (Log.on) Log.log(this, "got ServerKeyExchange");
410                     serverKeyExchange = rec;
411                     break;
412
413                 case 13:
414                     if (Log.on) Log.log(this, "got Request for Client Certificates");
415                     cert_requested = true;
416                     break;
417                     
418                 case 14: if (Log.on) Log.log(this, "  ServerHelloDone"); return;
419                 default: throw new SSLException("unknown handshake of type " + rec[0]);
420                 }
421             }
422         }
423      
424         public void readServerFinished() throws IOException {
425             
426             byte[] rec = readHandshake();
427             if (rec[0] != 20) throw new SSLException("expecting server Finished message, but got message of type " + rec[0]);
428
429             byte[] expectedFinished = concat(new byte[][] {
430                 md5(new byte[][] { master_secret, pad2,
431                                    md5(new byte[][] { handshakes, new byte[] { (byte)0x53, (byte)0x52, (byte)0x56, (byte)0x52 },
432                                                       master_secret, pad1 }) }),
433                 sha(new byte[][] { master_secret, pad2_sha,
434                                    sha(new byte[][] { handshakes, new byte[] { (byte)0x53, (byte)0x52, (byte)0x56, (byte)0x52 },
435                                                       master_secret, pad1_sha } ) } ) } );
436
437             for(int i=0; i<expectedFinished.length; i++)
438                 if (expectedFinished[i] != rec[i + 4])
439                     throw new SSLException("server Finished message mismatch!");
440
441             if (Log.on) Log.log(this, "server finished message checked out okay!");
442         }
443    
444     }
445     
446     class SSLOutputStream extends OutputStream {
447         
448         /** the underlying outputstream */
449         DataOutputStream raw;
450         
451         /** the sequence number for sending */
452         public long seq_num = 0;
453
454         /** the encryption engine for sending */
455         RC4Engine rc4 = null;
456         
457         public SSLOutputStream(OutputStream raw) { this.raw = new DataOutputStream(raw); }
458         public void flush() throws IOException { raw.flush(); }
459         public void write(int b) throws IOException { write(new byte[] { (byte)b }, 0, 1); }
460         public void write(byte[] b, int off, int len) throws IOException { write(b, off, len, (byte)23); }
461         public void close() throws IOException {
462             write(new byte[] { 0x1, 0x0 }, 0, 2, (byte)21);
463             raw.close();
464         }
465         
466         /** writes a single SSL Record */
467         public void write(byte[] payload, int off, int len, byte type) throws IOException {
468
469             // largest permissible frame is 2^14 octets
470             if (len > 1 << 14) {
471                 write(payload, off, 1 << 14, type);
472                 write(payload, off + 1 << 14, len - 1 << 14, type);
473                 return;
474             }
475
476             raw.writeByte(type);
477             raw.writeShort(0x0300);
478
479             if (rc4 == null) {
480                 raw.writeShort(len);
481                 raw.write(payload, off, len);
482
483             } else {
484                 byte[] MAC = computeMAC(type, payload, off, len, client_write_MAC_secret, seq_num);
485                 byte[] encryptedPayload = new byte[MAC.length + len];
486                 rc4.processBytes(payload, off, len, encryptedPayload, 0);
487                 rc4.processBytes(MAC, 0, MAC.length, encryptedPayload, len);
488                 raw.writeShort(encryptedPayload.length);
489                 raw.write(encryptedPayload);
490
491             }
492
493             seq_num++;
494         }
495
496         /** tacks a handshake header onto payload before sending it */        
497         public void writeHandshake(int type, byte[] payload) throws IOException {
498             byte[] real_payload = new byte[payload.length + 4];
499             System.arraycopy(payload, 0, real_payload, 4, payload.length);
500             real_payload[0] = (byte)(type & 0xFF);
501             intToBytes(payload.length, real_payload, 1, 3);
502             handshakes = concat(new byte[][] { handshakes, real_payload });
503             write(real_payload, 0, real_payload.length, (byte)22);
504         }
505
506         public void sendClientHandshakes() throws IOException {
507             
508             if (Log.on) Log.log(this, "shaking hands");
509             if (cert_requested) {
510                 if (Log.on) Log.log(this, "telling the server we have no certificates");
511                 writeHandshake(11, new byte[] { 0x0, 0x0, 0x0 });
512             }
513             
514             // generate the premaster secret
515             byte[] pre_master_secret = new byte[48];
516             pre_master_secret[0] = 0x03;                            // first two bytes of premaster secret are our version number
517             pre_master_secret[1] = 0x00;
518             getRandomBytes(pre_master_secret, 2, pre_master_secret.length - 2);
519
520             // encrypt and send the pre_master_secret            
521             try {
522                 byte[] encrypted_pre_master_secret;
523
524                 SubjectPublicKeyInfo pki = server_cert.getSubjectPublicKeyInfo();
525                 RSAPublicKeyStructure rsa_pks = new RSAPublicKeyStructure((DERConstructedSequence)pki.getPublicKey());
526                 BigInteger modulus = rsa_pks.getModulus();
527                 BigInteger exponent = rsa_pks.getPublicExponent();
528
529                 if (serverKeyExchange != null) {
530
531                     AsymmetricBlockCipher rsa = new PKCS1(new RSAEngine());
532                     rsa.init(false, new RSAKeyParameters(false, modulus, exponent));
533
534                     int modulus_size = ((serverKeyExchange[4] & 0xff) << 8) | (serverKeyExchange[5] & 0xff);
535                     byte[] b_modulus = new byte[modulus_size];
536                     System.arraycopy(serverKeyExchange, 6, b_modulus, 0, modulus_size);
537                     modulus = new BigInteger(1, b_modulus);
538
539                     int exponent_size = ((serverKeyExchange[6 + modulus_size] & 0xff) << 8) | (serverKeyExchange[7 + modulus_size] & 0xff);
540                     byte[] b_exponent = new byte[exponent_size];
541                     System.arraycopy(serverKeyExchange, 8 + modulus_size, b_exponent, 0, exponent_size);
542                     exponent = new BigInteger(1, b_exponent);
543
544                     byte[] server_params = new byte[modulus_size + exponent_size + 4];
545                     System.arraycopy(serverKeyExchange, 4, server_params, 0, server_params.length);
546
547                     byte[] expectedSignature = concat(new byte[][] { md5(new byte[][] { client_random, server_random, server_params } ),
548                                                                      sha(new byte[][] { client_random, server_random, server_params } ) } );
549
550                     byte[] recievedSignature = rsa.processBlock(serverKeyExchange, 6 + server_params.length,
551                                                                 serverKeyExchange.length - 6 - server_params.length);
552
553                     for(int i=0; i<expectedSignature.length; i++)
554                         if (expectedSignature[i] != recievedSignature[i])
555                             throw new SSLException("ServerKeyExchange message had invalid signature " + i);
556
557                     if (Log.on) Log.log(this, "ServerKeyExchange successfully processed");
558                 }
559
560                 AsymmetricBlockCipher rsa = new PKCS1(new RSAEngine());
561                 rsa.init(true, new RSAKeyParameters(false, modulus, exponent));
562
563                 encrypted_pre_master_secret = rsa.processBlock(pre_master_secret, 0, pre_master_secret.length);
564                 writeHandshake(16, encrypted_pre_master_secret);
565
566             } catch (Exception e) {
567                 SSLException t = new SSLException("exception encrypting premaster secret");
568                 t.fillInStackTrace();
569                 throw t;
570             }
571             
572             // ChangeCipherSpec
573             if (Log.on) Log.log(this, "Handshake complete; sending ChangeCipherSpec");
574             write(new byte[] { 0x01 }, 0, 1, (byte)20);
575             seq_num = 0;
576
577             // compute master_secret
578             master_secret = concat(new byte[][] {
579                 md5(new byte[][] { pre_master_secret,
580                                    sha(new byte[][] { new byte[] { 0x41 }, pre_master_secret, client_random, server_random })}),
581                 md5(new byte[][] { pre_master_secret,
582                                    sha(new byte[][] { new byte[] { 0x42, 0x42 }, pre_master_secret, client_random, server_random })}),
583                 md5(new byte[][] { pre_master_secret,
584                                    sha(new byte[][] { new byte[] { 0x43, 0x43, 0x43 }, pre_master_secret, client_random, server_random })})
585                 } );
586             
587             // construct the key material
588             byte[] key_material = new byte[] { };
589             for(int i=0; key_material.length < 72; i++) {
590                 byte[] crap = new byte[i + 1];
591                 for(int j=0; j<crap.length; j++) crap[j] = (byte)(((byte)0x41) + ((byte)i));
592                 key_material = concat(new byte[][] { key_material,
593                                                    md5(new byte[][] { master_secret,
594                                                                       sha(new byte[][] { crap, master_secret, server_random, client_random }) }) });
595             }
596
597             client_write_key = new byte[export ? 5 : 16];
598             server_write_key = new byte[export ? 5 : 16];
599
600             System.arraycopy(key_material, 0,  client_write_MAC_secret, 0, 16);
601             System.arraycopy(key_material, 16, server_write_MAC_secret, 0, 16);
602             System.arraycopy(key_material, 32, client_write_key, 0, export ? 5 : 16);
603             System.arraycopy(key_material, export ? 37 : 48, server_write_key, 0, export ? 5 : 16);
604             
605             if (export) {
606                 // see SSLv3 spec, 6.2.2 for explanation
607                 byte[] client_untrimmed = md5(new byte[][] { concat(new byte[][] { client_write_key, client_random, server_random } ) });
608                 byte[] server_untrimmed = md5(new byte[][] { concat(new byte[][] { server_write_key, server_random, client_random } ) });
609                 client_write_key = new byte[16];
610                 server_write_key = new byte[16];
611                 System.arraycopy(client_untrimmed, 0, client_write_key, 0, 16);
612                 System.arraycopy(server_untrimmed, 0, server_write_key, 0, 16);
613             }
614
615             rc4 = new RC4Engine();
616             rc4.init(true, new KeyParameter(client_write_key));
617             is.rc4 = new RC4Engine();
618             is.rc4.init(false, new KeyParameter(server_write_key));
619             
620             // send Finished
621             writeHandshake(20, concat(new byte[][] { 
622                 md5(new byte[][] { master_secret, pad2, 
623                                    md5(new byte[][] { handshakes, new byte[] { (byte)0x43, (byte)0x4C, (byte)0x4E, (byte)0x54 },
624                                                       master_secret, pad1 }) }),
625                 sha(new byte[][] { master_secret, pad2_sha,
626                                    sha(new byte[][] { handshakes, new byte[] { (byte)0x43, (byte)0x4C, (byte)0x4E, (byte)0x54 },
627                                                       master_secret, pad1_sha } ) })
628             }));
629             raw.flush();
630             if (Log.on) Log.log(this, "wrote Finished message");
631
632         }
633         
634         public void writeClientHello() throws IOException {
635             
636             if (Log.on) Log.log(this, "sending ClientHello");
637             int unixtime = (int)(System.currentTimeMillis() / (long)1000);
638             
639             byte[] out = new byte[] {
640                 0x03, 0x00,                     // client version (SSLv3.0)
641                 
642                 // space for random bytes
643                 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
644                 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
645                 0x0, 0x0, 0x0, 0x0,
646                 
647                 0x0,                            // empty vector for sessionid
648                 0x0, 0x4, 0x0, 0x4, 0x0, 0x3,   // we support two ciphersuites: SSL_RSA_WITH_RC4_128_MD5 and SSL_RSA_EXPORT_WITH_RC4_40_MD5
649                 0x1, 0x0                        // we only support one compression method: none
650             };
651             
652             // don't need to use secure random here since it's sent in the clear
653             Random rand = new Random(System.currentTimeMillis());
654             rand.nextBytes(client_random);
655             intToBytes(unixtime, client_random, 0, 4);
656             System.arraycopy(client_random, 0, out, 2, client_random.length);
657             
658             writeHandshake(1, out);
659             flush();
660         }
661     }
662
663     // Static Helpers ////////////////////////////////////////////////////////////////////
664
665     /** copy the least significant num bytes of val into byte array b, startint at offset */
666     public static void intToBytes(long val, byte[] b, int offset, int num) {
667         for(int i=0; i<num; i++)
668             b[offset + num - i - 1] = (byte)((val & (0xFFL << (i * 8))) >> (i * 8));
669     }
670
671     /** fills b with random bytes */
672     public static synchronized void getRandomBytes(byte[] b, int offset, int len) {
673         MD5Digest md5 = new MD5Digest();
674         byte[] b2 = new byte[16];
675         while(len > 0) {
676             md5.reset();
677             md5.update(randpool, 0, randpool.length);
678             intToBytes(randcnt++, b2, 0, 8);
679             md5.update(b2, 0, 8);
680             md5.doFinal(b2, 0);
681             int n = len < 16 ? len : 16;
682             System.arraycopy(b2, 0, b, offset, n);
683             len -= n;
684             offset += n;
685         }
686     }
687
688     public static byte[] computeMAC(byte type, byte[] payload, int off, int len, byte[] MAC_secret, long seq_num) {
689         byte[] MAC = new byte[16];
690         MD5Digest md5 = new MD5Digest();
691         md5.update(MAC_secret, 0, MAC_secret.length);
692         md5.update(pad1, 0, pad1.length);
693
694         byte[] b = new byte[11];
695         intToBytes(seq_num, b, 0, 8);
696         b[8] = type;
697         intToBytes(len, b, 9, 2);
698         md5.update(b, 0, b.length);
699
700         md5.update(payload, off, len);
701         md5.doFinal(MAC, 0);
702         md5.reset();
703         md5.update(MAC_secret, 0, MAC_secret.length);
704         md5.update(pad2, 0, pad2.length);
705         md5.update(MAC, 0, MAC.length);
706         md5.doFinal(MAC, 0);
707
708         return MAC;
709     }
710
711     public static byte[] concat(byte[][] inputs) {
712         int total = 0;
713         for(int i=0; i<inputs.length; i++) total += inputs[i].length;
714         byte[] ret = new byte[total];
715         int pos = 0;
716         for(int i=0; i<inputs.length; i++) {
717             System.arraycopy(inputs[i], 0, ret, pos, inputs[i].length);
718             pos += inputs[i].length;
719         }
720         return ret;
721     }
722     
723     SHA1Digest master_sha1 = new SHA1Digest();
724     public byte[] sha(byte[][] inputs) {
725         master_sha1.reset();
726         for(int i=0; i<inputs.length; i++) master_sha1.update(inputs[i], 0, inputs[i].length);
727         byte[] ret = new byte[master_sha1.getDigestSize()];
728         master_sha1.doFinal(ret, 0);
729         return ret;
730     }
731     
732     MD5Digest master_md5 = new MD5Digest();
733     public byte[] md5(byte[][] inputs) {
734         master_md5.reset();
735         for(int i=0; i<inputs.length; i++) master_md5.update(inputs[i], 0, inputs[i].length);
736         byte[] ret = new byte[master_md5.getDigestSize()];
737         master_md5.doFinal(ret, 0);
738         return ret;
739     }
740
741     // FEATURE: improve error reporting in here
742     /** returns true iff certificate "signee" is signed by public key "signer" */
743     public static boolean isSignedBy(X509CertificateStructure signee, SubjectPublicKeyInfo signer) throws SSLException {
744
745         Digest hash = null;
746
747         String signature_algorithm_oid = signee.getSignatureAlgorithm().getObjectId().getId();
748         if (signature_algorithm_oid.equals("1.2.840.113549.1.1.4")) hash = new MD5Digest();
749         else if (signature_algorithm_oid.equals("1.2.840.113549.1.1.2")) hash = new MD2Digest();
750         else if (signature_algorithm_oid.equals("1.2.840.113549.1.1.5")) hash = new SHA1Digest();
751         else throw new SSLException("unsupported signing algorithm: " + signature_algorithm_oid);
752
753         try {
754             // decrypt the signature using the signer's public key
755             byte[] ED = signee.getSignature().getBytes();
756             SubjectPublicKeyInfo pki = signer;
757             RSAPublicKeyStructure rsa_pks = new RSAPublicKeyStructure((DERConstructedSequence)pki.getPublicKey());
758             BigInteger modulus = rsa_pks.getModulus();
759             BigInteger exponent = rsa_pks.getPublicExponent();
760             AsymmetricBlockCipher rsa = new PKCS1(new RSAEngine());
761             rsa.init(false, new RSAKeyParameters(false, modulus, exponent));
762             
763             // Decode the embedded octet string
764             byte[] D = rsa.processBlock(ED, 0, ED.length);
765             BERInputStream beris = new BERInputStream(new ByteArrayInputStream(D));
766             DERObject derob = beris.readObject();
767             DERConstructedSequence dercs = (DERConstructedSequence)derob;
768             DEROctetString deros = (DEROctetString)dercs.getObjectAt(1);
769             byte[] MD = deros.getOctets();
770             
771             // generate our own hash
772             ByteArrayOutputStream baos = new ByteArrayOutputStream();
773             DEROutputStream dos = new DEROutputStream(baos);
774             dos.writeObject(signee.getTBSCertificate());
775             dos.flush();
776             byte[] b = baos.toByteArray();
777             hash.update(b, 0, b.length);
778             byte[] md_out = new byte[MD.length];
779             hash.doFinal(md_out, 0);
780             
781             // compare our hash to the signed hash
782             for(int j=0; j<MD.length; j++) if (md_out[j] != MD[j]) return false;
783             return true;
784
785         } catch (Exception e) {
786             return false;
787
788         }
789     }
790
791     // Embedded Trusted Public Keys //////////////////////////////////////////////
792
793     /** base64-encoded sequence of DER-encoded PKCS7 certs for all the "trusted root CA's" included with IE5.5 */
794     static String[] base64_encoded_trusted_CA_public_keys = new String[] {
795
796         // CN=ABA.ECOM Root CA
797         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdMR4HlVQwcITMsFQgDiDYNGPe" +
798         "STurYG0w1ZvT7BzkNnAYohqO+8zNCizLBVllOEZgUA2kRJgNhUCqUlhpTtY1b/cGyjoRnS" +
799         "eL5oKkReL8/MGF5HvDqxRj0e8LksNF+MfEwIKZ1AVes8fYPetfD3ioMOoUy0OqWzX1oil+" +
800         "wZm8EFaP3mt6mRlCzkeEgkGiUZOuuVnDkKis9CsvAc1V/7a+1oVns5LHI4sO6TqdN7dzzr" +
801         "cQOpOEoWbIkqytozE3nCVYztnLvyy1sQ+C5hNcYpTCrQKmPRZVm0+M359ACEtldChZ0yqP" +
802         "kqVPv/eEG8vXEo9LuQvP+WNATjRZ6hRihAgQIDAQAB",
803
804         // O=ViaCode
805         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCws2enlrV2g+kWA9kaoHbVdOecBdJRP9" +
806         "tPGaGoOU7LJH1hxB5qIK4Pgd7quDn9Gx9rNkDtTSEl8qPZoVHYbMAblvjUQpTUp84bj9NU" +
807         "JqKE7zKFr0o/8TI2rz3mOifrA8IlfvRhK62KGkvmmzZo1C/l0oiU3Baq2sIVTGzD4RmRyQ" +
808         "IBAw==",
809
810         // CN=Xcert EZ by DST
811         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVQY3rS/963odKrti3yPwtR1Gt" +
812         "WEubZi/Inv5JdhkvsduOFaRzSengYi+9PqOMu4iwf3GqAXdwdaMBzUKTgg1ydA2FCTQ7/S" +
813         "GKIpdgVyqmu2aZireR4cZfVqi/zFFqqictpg7U5uGSV6Ch0w41CbQjxE66GwIB7bAn7+PR" +
814         "+/0ACK20B2philFadXtlLCAReYd4+KgcYatGoq5q+p1gCsz9gVSXzbG6H+gfqH+dOQwQLA" +
815         "+dBC6ZFoJV/Gv4c56ZUAYCi/gyzA51621zYW52CHdujnJ7IlDYt65aod5VnNzgsOb8bInO" +
816         "MQ2YU507eb+sa6fHTSXXVWq3SkolG/UnzucQIDAQAB",
817
818         // CN=Certiposte Classe A Personne
819         "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQAox3xaJSN48rMAR0Biy2+MQlCfnl" +
820         "7UXA5lC1hWlSvjRtBhNuAtRpuCy5Hu0pV8mpKvBAp+pp/g17HDRfmYQRs5redW19m2f867" +
821         "OS4sO8+2cwODzhNdMmpjottb+Esz6FBsy6gX7J6TuWwGSyYLdx6e+eWMiTfS0bv9qYwrLJ" +
822         "wQMdhLjM23cX44LCnjF7JP6FK245I80v3hAtphEHTSGvPI0dFmB1/EhGNpva5s3GUjHLf7" +
823         "98YTLoN+P6nlCyBtAQo34lzait4icOkN4HQ9xOtxm2Eq4g0Ui0xGN0wm0mjWVsNXqqJgN6" +
824         "9fnaCzgILmQypMgAAJUNmoanNtA/5ec5LlAgMBAAE=",
825
826         // CN=Certiposte Serveur
827         "MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQA+p3gzOJHiylaV0ZFGsiPcpVZ/D8" +
828         "eXuOKekS4oFi6O80e2XIPE8Ob+ZxqTZH1ACdgdaADs1BHu2GOJAyPphF/HVQ5K4nK7KcFV" +
829         "ZHao45LN9/ZuQlYYUjOJ+YAUqBlRfsd3v3qoMcB9F25DTtVmyQU+S+Ll4lUbdKpRHarMmB" +
830         "F3pOvbKg4nx9XNSOzcfk5J50HNmQvRS14YGw06CpstmznHQAzQdgd8fI9+XHKOh9W+8qa5" +
831         "3r/dnxJ5R3zFyZdARgCS0xNak0+dfthfTMFdSEnZLZg8/MynhyHwPo5yfVk4NhYaDEi+of" +
832         "LVPqgWDCBZz84PM4M9rav1/93X/WkIiADvAgMBAAE=",
833
834         // OU=Certisign - Autoridade Certificadora - AC2
835         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5MMyl65DWVpRnM4mDbUa+cJeTF04KJ3" +
836         "DOycXyxdIt0RGcdzJsdNOSb/rp1bhhmqpMEz41OvDuCTbZ0Zcxx16sQUm/SG1OIFPJe2qj" +
837         "ljFrsm6ozy9yTAatMs9aCPN9EJyqu7pz+fPwuCRvqGW2Iv4FWxBVRMIDHa3RIswIbfuMyw" +
838         "IDAQAB",
839
840         // OU=Certisign - Autoridade Certificadora - AC4
841         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDsg9TMg5A/X+y+wenQx1hGWR/xk0qyFx" +
842         "MLzymZqwRFM+PRXr68jiV3Yt2bkpsxCkBFedXys91suUD9mH9Aoi3pspO9S9XB3unR+nH3" +
843         "P0G89BSvzWvIOUqdYGW0hNBqQeljrptp6rlGHNsYCDtiTN5B156GfxNyEdTc6t5gpbvdGw" +
844         "IDAQAB",
845
846         // OU=Certisign Autoridade Certificadora AC1S
847         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwwJXro8VB+JtvcWOOkRFX+QPHaJoanG" +
848         "Hwww8Ml2KIfiYBNX398W9PF5WqfvK7vO/idnNhlTZRgz6E6D+6VzY3lBNskmQflA3rVC9R" +
849         "WuUoXvCShufkbSF6XzcL51u9LQKogfk/yxTIvKTF49HLN9yr5Yeq8guYLnrPzB7Cf+j9AQ" +
850         "IDAQAB",
851
852         // OU=Certisign Autoridade Certificadora AC3S
853         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOZE7Wz658mCeY7yjvujTDNRqd0mYecf" +
854         "Hkli0nFzmQRY8t7+bVR6nhg4F8Pihx+oC7XfhDaxkQwZhvFZ4trklkROyEGmlZFleyPZLY" +
855         "Zku/ma1DGMc4yYuOLAQus0trk/adH4SyzeYAwr42pbxZtZ+LGSD/5agopFW2irayxddE4w" +
856         "IDAQAB",
857
858         // O=Certplus, CN=Class 1 Primary CA
859         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw2spyC7HrnxSBemTiVYKWnnJzN" +
860         "wl74eKLQXYgRcEGzpF+HkODUnUgUHIq0X7dcgV8uLQvNlhbISkExmn2fnySdxMD8Z9V7QT" +
861         "3B4JcSk2nYBY9BvYiRTr09KTSyrxd+dqZb0Z5ar9DEpj4cKZtA8EtlobNjw3PL/F5V7xX1" +
862         "cOH8f9LOfkb2qbYpY5EZtm8Cy2UtzhJ//bbf7rq2MUHWOIY+IWDPkgVA+b3RVqdoNPvSeL" +
863         "U6Y30ofyR1BSO2bp0XgaG7I7afBZPDhb0SpMM14Oylal7S1bgoNN1jhOila2ai8kaxIwpi" +
864         "rerwy7qkQSHBPFZQ/j/dgaMUvkPwx8RegWMwIDAQAB",
865
866         // O=Certplus, CN=Class 2 Primary CA
867         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FCW0BL4NdIIeHq2UnD9b+7PuR" +
868         "HLXXfh7Ol+BI3WzG9zQ1dgrDMKROwDXxyAJJHlqJFWEoL34Cv0265hLokQjWtsurMCvdU2" +
869         "xUg3I+LwWjdSMxcS4tFgTb4vQRHj9hclDIuRwBuZe5lWDa/u0rxHV+N5SXs0iSckhN6x7O" +
870         "lYTv5O31q+Qa2sCMUYDu/SU+5s0J0SARON3IBi95WpRIhKcU5gVZ7bIxl5VgcMP2MLXLDi" +
871         "vn4V/JQzWEE4dMThj4vfJqwftYs7t0NZa7Akpm2Qi8Ry6l0zmLfL3l5775TxGz7KySHBxZ" +
872         "gCqqL2W3eb9X6WVTQcZ2nA8ULjR6z8KBxmVQIDAQAB",
873
874         // O=Certplus, CN=Class 3 Primary CA
875         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt5QwbtBM2X5eYOVvuybKUm9rbI" +
876         "WZpQvvABpvG01YtRgH+t17+MssUt38nLiNUum5sgt89Y9bmUgJWN7NSJWGJYkFNCcwC1e2" +
877         "DHdjKctsqj65mVESDZhwdkdM+UmWIgj5a+qqADajFaccHp+Mylp5eyHaiR9jfnmSUAkEKK" +
878         "3O420ESUqZsT9FCXhYIO+N/oDIBO0pLKBYjYQCJZc/oBPXe4sj45+4x7hCQDgbkkq9SpRV" +
879         "x1YVDYF3zJ+iN4krW4UNi3f4xIv7EMuUx+kaVhKXZhTEu9d9bQIbv3FiJhjpSYr6o97hhK" +
880         "2AykriIoxqCGGDsiLHCYg4Vl3RMavwCZ8TWQIDAQAB",
881
882         // CN=Autoridad Certificadora de la Asociacion Nacional del Notariado Mexicano
883         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7tlrVYRxJvaOrUG71tLeY+ryP2" +
884         "XyOxPBrlEm9L94j8ZMSay/Qd71KMco55/XgOXU7iMrk5U9yY9q9coA6RDHiIIabqNf8DRS" +
885         "ISVoKPiV8ICVoiyxP2r2KNbihP0WZ5wluXXb5cZZA7SrQgeI1VxIRaIJA8muZ5KoolPHyq" +
886         "t+mhKVWgVXjRBklicRsOYyMFvNPQygGxMtuxqr3TnOkmuiBNQTX213Z1Q5qHtpisZfeMoH" +
887         "GGlu+cDT0IqOrx4waO742KhmDIR9I2qJPGJNFHSs25uc/LCD/gcw8factEjI5jpCJQko91" +
888         "bCsdejmHcCh+qKwV3axIonB4VeSExVKEDtCQIDAQAB",
889
890         // O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
891         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhA" +
892         "wL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lw" +
893         "dd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpw" +
894         "IDAQAB",
895
896         // C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
897         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhA" +
898         "wL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lw" +
899         "dd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpw" +
900         "IDAQAB",
901
902         // C=FR, O=Certplus, CN=Class 3P Primary CA
903         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqzf/62CbQXhp9UlYsN4fcWmmK+" +
904         "OuUMapvJPpIL7kxBOCVu/wQzIJypt1A498T+HgT3aeC61kehQ6mp2/LxYLZRyp7py84xpl" +
905         "y0+F6pJWdWbWVUDv+8zWOD+rHO9CjRmJ9reVhsKnHen3KfEq2WV5/Cv1jsoad36e6Kz5Zr" +
906         "9F++gTnV+2c+V9e477EnRdHwZehRumXhhEALq8027RUg4GrevutbTBu7zrOA9IIpHHb9K4" +
907         "cju6f8CNbLe8R3MhKoX/rNYoohnVl2o6uaxtRezmTcPbqF3FXYKYrEpaquYrCAwQdLxi9j" +
908         "pJBGbYURwmpth1n5y/rmBRPVy8ok97iWfNUwIDAQAB",
909
910         // C=FR, O=Certplus, CN=Class 3TS Primary CA
911         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWWaI0MAPPklAUOYW0Y0N2c39F" +
912         "tXjqPezYwvQbMVPeWYi/LMXKfHrzXHs6dPxxApV+kDiYNyBnZSwXACN0Dt8M6LsbGJrAKo" +
913         "W93c1UNFBtwotulRG2ru83tIxZ0Rro2mcpPAJUKRqD5G4mhMgUCwQtN6vntH0kdQDKQSps" +
914         "rkEtDAfDo8AanKApbeglrF+xm6PJzYD3QfmBiulFAyB1IQEUpL7FhVLNSeS5R7BdJy3wbw" +
915         "jcsInuTutEStgvEbYWrxs/gWMTZCJLqQv7V+YW7CWQxUebRMiCgezBvfhIsjyL6vB/KRst" +
916         "qNyoxffCg8fIlsBlm9Ps7FgtNqyaxoVe7FrwIDAQAB",
917
918         // C=US, O=RSA Data Security, Inc., OU=Commercial Certification Authority
919         "MIGbMA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AKT7gWJ7zhAn3ej3vmxuxnCZ27jVBQNpKI" +
920         "Kccn+WP47srCmSP4oU+EJ2vr1dA7mQ1NC8BrJRM1/Ewr+2i4+ZtmIiYN3b3yCCtMqiLy1Q" +
921         "7ZQy3uBVjdRo4uBM0s0FFi6VZlxhUjgeUaiCocTvJekK5osrjjFm2fjZ/b07adnrAgMBAA" +
922         "E=",
923
924         // C=DE, O=Deutsche Telekom AG, OU=T-TeleSec Trust Center, CN=Deutsche Telekom Root CA 1
925         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQ3ZsMoBdERA+vIUBzZ1bwPmloEbrZN/" +
926         "KBrsMkrGmhzfymGFVW/4ufMsHb53gsOdtggUGl79PNgI0YPOJSDAuf92Se5aDwuGFi9L/g" +
927         "o9pYK/0VBGu9Op58nfI92OSVw+xOwvFlqwxL7EeCW+LhUHXY9mG0GFztM6BLHoP7T4S8eQ" +
928         "IDAQAB",
929
930         // C=DE, O=Deutsche Telekom AG, OU=T-TeleSec Trust Center, CN=Deutsche Telekom Root CA 2
931         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqwujNeCLKRSxFIWvPBDkOW81XU" +
932         "qu3ephjZVJ9G9koxpgZqSpQCKE2dSl5XiTDmgBrblNXDrO07ioQkDfz6O6gllqkhusHJra" +
933         "CCslJ/lpI0fx4Ossepv1EwLQfjR8wp48AFmr9doM9TI8K6xQ2tbD3oOUyqgMmTIOCEhWW2" +
934         "r72uFYWAFJX3JBPBUGAY5draq4k7TNnuun6GotUjTbOu9cdVHa2/Mx+e5xmDLEVBVEDPmb" +
935         "Ve2t3xgIoKOGiknuUwWPGUzV3lh5m9JqHEKrxdWnz2gPluThYZh2YciRfNY+AOKRUIfhnQ" +
936         "rmrZfSHcY6fcu82gM01Y5bAfVqB7cWtm5KfwIDAQAB",
937
938         // C=US, O=Digital Signature Trust Co., OU=DST (ANX Network) CA
939         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC0SBGAWKDVpZkP9jcsRLZu0XzzKmueEb" +
940         "aIIwRccSWeahJ3EW6/aDllqPay9qIYsokVoGe3eowiSGv2hDQftsr3G3LL8ltI04ceInYT" +
941         "BLSsbJZ/5w4IyTJRMC3VgOghZ7rzXggkLAdZnZAa7kbJtaQelrRBkdR/0o04JrBvQ24JfQ" +
942         "IBAw==",
943
944         // OU=National Retail Federation, CN=DST (NRF) RootCA
945         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2aybd/pQ08zcuUCsuXJqAIcj/A" +
946         "+WIdAmr+TitV/606Z9ITAuzBeCj5h0/Gekpt+Il6JCKfWn2xGT+14jMMKqvCLnQRvl7SXe" +
947         "yD/b3ldFeEBGg7LVGj3fD0Vt1WMCddgvxm6rlZF0Nw3LTQlc0dRbOtrdDshrmdjVOczfhV" +
948         "XEklMCo+H3gMlwo9rcM8R/okcIHDWWH6EDHDCD9MTM/5jDsEZEosC/rdvSgfZMmCynXiTz" +
949         "hspj1bp98JrAStAbWO7sqWfPaQJsIsBgLCzRyCDqyC373Zy7y1FM3OdXBDtUmxGlMnTsdA" +
950         "HzkBVbL3wsk2W5Zme0gYg15Z6RGH+BqEHIywIDAQAB",
951
952         // OU=United Parcel Service, CN=DST (UPS) RootCA
953         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7xfsrynm2SsnwNt7JJ9m9ASjwq" +
954         "0KyrDNhCuqN/OAoWDvQo/lXXdfV0JU3SvbYbJxXpN7b1/rJCvnpPLr8XOzC431Wdcy36yQ" +
955         "jk4xuiVNtgym8eWvDOHlb1IDFcHfvn5KpqYYRnA/76dNqNz1dNlhekA8oZQo6sKUiMs3FQ" +
956         "UZPJViuhwt+yiM0ciekjxbEVQ7eNlHO5stSuY+e2vf9PYFzyj2upg2AJ48N4UKnN63pIXF" +
957         "Y/23YhRtFx7MioCFQjIRsCHinXfJgBZBnuvlFIl/t8O8T8Gfh5uW7GP2+ZBWDpWjIwqMZN" +
958         "qbuxx3sExd5sjo9X15LVckP8zjPSyYzxKfFwIDAQAB",
959
960         // CN=Autoridad Certificadora del Colegio Nacional de Correduria Publica Mexicana
961         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmO0dhYH7/jd0viOAJ18bQX6856" +
962         "WK2HNdlkjqq1iqfaUdz/4gCtnydQnts9X9+JMqGaleqLEU8tZChkFBXk/FVqeaokJvLihI" +
963         "6i6r2cHZmvClnotdEWeaNzdTYGbxIv93d0fp3dwYRu4u3+LBluDqWN6H65OIaZmwPm52KU" +
964         "Bhwyhmc3+sMXb0OM3WMo9zMhAVNNJ8RND8eQwAnX0P4+P3RPWedEknrRvXMshTrm8qsNe1" +
965         "LRgsbjs6TUzb9Wi1L7AMkPk93HU2msLgv7uWiMJr7hjXTlA/V4tnaKS+AzNdWRI0if52yN" +
966         "kVdgFUZP2s41DvEMjQ7l/sHd9PBZg8tBReAQIDAQAB",
967
968         // CN=DST RootCA X1
969         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0sYmtuelPcHEaNVQb1PFb0kTCb" +
970         "ivLEiNFGqjF19a+dMudS/YKGLRky/8TdSrh+UIx5nnkj91vesltBXBmxk90kSN13QgbTcC" +
971         "j2mTW4rEGZ30sg78Fmy5sQWSg9GFLGCUPkVVoNmrCCHmYOg7dPKZUFFo0AMtsYC+o9hSsE" +
972         "TNQ0pwjliFleFOLNYtQW/WhOfImETKR9ssJKVpJs9ruCdiw/TJepIj7RNngq5FLkXlfnI/" +
973         "hZ2UYhDmPJGhrXcA4BXs84SAcnqObmCXxyRZEDSDW+GlpGm2VzUceFnG0y86c2fulMoEEw" +
974         "ViBnAjs/R87kXZZAtbSaqkQ84mxEQSbLjdeQIDAQAB",
975
976         // OU=DSTCA X2, CN=DST RootCA X2
977         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3HXwjMB1lprAYh8m98ThmurgVn" +
978         "Nbmc0BRKgIttWn2hoEGDmSSnijgcL1d3pQtHD/mqvGx8pug09CmPsmC9rcbdapmVVSZ+ko" +
979         "A5Lc5bAFmg8V+WtZclby+jn8qmjuDx8Qgy/8nfoXlt2C4+ZFfcBLgEQf7SzghP2RXJJUaS" +
980         "XlYmnc5e4AUr0zC611AoWnZFAtxRkZMMAm28nT/S6ZrVm1C03UQa6FSENZ3Leo4qLew4/X" +
981         "uKFipmhQUuTPMaeUhdqfRjIXVuXy62Y9Ev9D25jvd8/LgY00scZQSibR5D5BUK9sriI0Lt" +
982         "VrboO6ebh2ZUjaCSlkYyK5+0d2hYyGRMsJ2wIDAQAB",
983
984         // C=US, O=Digital Signature Trust Co., OU=DST-Entrust GTI CA
985         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC2HfdLjQ8T4xL1Cf4GMg6vTEH1fdRHPS" +
986         "oK34MF3t595gMW9lE6y0caSq1+xP0dtL50injdC4OOtIQTxPv4bSmuoeEPD0PjtV5gafqD" +
987         "lPx55tx27dFEK479Erv+F3cXDIntp+9RfcTtOMM7o3r74k2gYLXy/RNl08bsP741nD0i7w" +
988         "IBAw==",
989
990         // C=US, O=Digital Signature Trust Co., OU=DSTCA E1
991         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodG" +
992         "BmE5gGHKlREmlvMVW5SXIACH7TpWJENySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+Lth" +
993         "zfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQ" +
994         "IBAw==",
995
996         // C=US, O=Digital Signature Trust Co., OU=DSTCA E2
997         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+S" +
998         "SmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87e" +
999         "ZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQ" +
1000         "IBAw==",
1001
1002         // CN=Entrust.net Certification Authority (2048)
1003         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvw" +
1004         "tKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtL" +
1005         "A/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj/L24Sc" +
1006         "F2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLPKQP5L6RQstRIzgUy" +
1007         "VYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUiVBcAkCaTvA5JaJ" +
1008         "G/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQAB",
1009
1010         // CN=Entrust.net Client Certification Authority
1011         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/" +
1012         "Bo6oT9n3V5z8GKUZSvx1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDe" +
1013         "g7K6PvHViTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173iw" +
1014         "IBAw==",
1015
1016         // CN=Entrust.net Secure Server Certification Authority
1017         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO" +
1018         "2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc" +
1019         "1lB5gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQw" +
1020         "IBAw==",
1021
1022         // C=US, O=Equifax, OU=Equifax Secure Certificate Authority
1023         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBXbFYZwhi7qCaLR8IbZEUaJgKHv7aBG" +
1024         "8ThGIhw9F8zp8F4LgB8E407OKKlQRkrPFrU18Fs8tngL9CAo7+3QEJ7OEAFE/8+/AM3UO6" +
1025         "WyvhH4BwmRVXkxbxD5dqt8JoIxzMTVkwrFEeO68r1u5jRXvF2V9Q0uNQDzqI578U/eDHuQ" +
1026         "IDAQAB",
1027
1028         // C=US, O=Equifax Secure Inc., CN=Equifax Secure eBusiness CA-1
1029         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOLxm8F7d33pOpX1oNF080GgyY9CLZWd" +
1030         "TEaEbwtDXFhQMgxq9FpSFRRUHrFlg2Mm/iUGJk+f1RnKok2fSdgyqHCiHTEjg0bI0Ablqg" +
1031         "2ULuGiGV+VJMVVrFDzhPRvpt+C411h186+LwsHWAyKkTrL6I7zpuq18qOGICsBJ7/o+mAw" +
1032         "IDAQAB",
1033
1034         // CN=Baltimore EZ by DST
1035         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvMyzPUN5uEf5FbduJrFMkph57c" +
1036         "Vw8zrp1d0D9Co/YIyW5UcWAvc2svGeJoj1nkJlng+uf+PMsW4h9fGIInTWH7J3BDkyuke1" +
1037         "NcATXQFyowVDzE7aJpqHqGFj9GanwxVG6tHR6jDDu3Fqm8FDhsE5H8ZWYAIb/Ig6oJm7jN" +
1038         "d4YdBeV4+RO4CLbv/JZYEKObuQEyA1SD+l4b8twXGDhSDtIIfLtv4ZjATd7Sld3woSzolW" +
1039         "8h9aGTFYtv1jNurJI96nkZcnZXKZbMd6RMRfvpsfHsqeWBymqiNq4wYbkiTYVyIJUBWQRv" +
1040         "CDXraATBKBPWZvBFU6iGvQ71aHUKC51lUbnQIDAQAB",
1041
1042         // C=US, O=Equifax Secure, OU=Equifax Secure eBusiness CA-2
1043         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkOTmTHlIGGyg2+LKjKcXtjrIRvf7r57" +
1044         "R0wo//BefZnQa/Esg/DvLW0SSyEd7RcwmK1LEsmAkNHlBGsoOmRY1iaLuFGyBwMqpAzaaW" +
1045         "X8RxNz8E87dBJDkHGh4uYVigEgvlpd/Fq+o3ccwcyDc6uZdSp6zFaiSUTpx7z8Bq1t8hvQ" +
1046         "IDAQAB",
1047
1048         // C=US, O=Equifax Secure Inc., CN=Equifax Secure Global eBusiness CA-1
1049         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC65xeQAmWxNFU8ScJR1d+n0TeP0eeBc0" +
1050         "FSYJudoRcmeK3HsegmlDK13jONOi/b8pp6WnOYo1zp+4pzG1znw7+AbM2p9NYrwPf5mapj" +
1051         "orFHAg/U5FE6EjxsilpUhHDbwcWQz3JFy6hZwM0znT+jluuFMyEcPh4+YG52nGeFxcjDYQ" +
1052         "IDAQAB",
1053
1054         // O=EUnet International, CN=EUnet International Root CA
1055         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeQTvZQUmLKJxZFPdQaCh7TQhcZ/+FHg" +
1056         "umzzoyArB8fEqftokCIQxKmYvLZFF+eFq2XqlTt+/vx9+lIVmXTuIH5S18GdUqysgz05YQ" +
1057         "Lt2gAJ/9yuhhqVPKth0YPpwR4GPnKmdbyESV8BNVSLu+VbhnN83LABMN/E9pFGpRlOy8Jw" +
1058         "IDAQAB",
1059
1060         // CN=FESTE, Public Notary Certs, EmailAddress=feste@feste.org
1061         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhg/ObLsnn4cf0VAXNdkD+tLMTvucVXo" +
1062         "Ym6EB3GlU/0QMmjPqHX6TF+f61MonGf0GR2BVATnBS8PHa+GI1mV4clFNhzD5iwINdWNH4" +
1063         "SBFxbPewd+EYl7QHKDCRMcdPVPOEnsxZiUVtfrTJ245ClWbU3x4YTfylD9YahDnEyvK98w" +
1064         "IDAQAB",
1065
1066         // CN=FESTE, Verified Certs
1067         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDqY58fOBqEBISzS5MZhKJ7YsOnqyzsYE" +
1068         "5VEeIEMicgNfkaeB8nZ6fggrAF6Capm4pEVr9LhFOjIqYOFlO5f68QyDMYVNnGTHzRW1ZS" +
1069         "U4amWz8T8sMB0jGhM1y8XeTcYjzKI5dPcPuBjrDZnq+T6raxJI0ELVFDPDjsJ0Nxh+g8xw" +
1070         "IDAQAB",
1071
1072         // CN=First Data Digital Certificates Inc. Certification Authority
1073         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDfHBQeCbm/pEByIJl5toQi9NeFksUEJO" +
1074         "gHLgLkF5UFN5V2Pfyx5Q+HDmK5LDCXJuELFWcAphXe6I3LlewCWFLAR2UzTFafCh8EwDdQ" +
1075         "gVe63/rya2fry9CAD9lXlRBlewZFWOuutF7jkxUrmby2KS/7Qp9HKy5M6zQoMpkO7/9voQ" +
1076         "IBAw==",
1077
1078         // C=ES, O=FNMT, OU=FNMT Clase 2 CA
1079         "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCYP60ZNpM9Pv52QhT9NW/x+q0ieljjRt" +
1080         "Bdxlr5Yi2PMV7+tDD+UHSs1p0d4GLGSd0UEn1xC6wGwT/XBofgkInW5eMDsvInsZ8zyKpr" +
1081         "NkqjxD95QZ2JRi8rPmPUOFaRqh2xDUJ1TfOHTuMPTcy0bL9iE4fq0JuOtuL/GfSUCdWWYQ" +
1082         "IBAw==",
1083
1084         // CN=Belgacom E-Trust Primary CA
1085         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2bmz1U9qTcVB0HsEYWqLcYEH2mTjWG" +
1086         "4nVcKtzhew/PqSjQjwHHL/ssMx/uBqh5dMzENXpyh5OrWDXaQdavFqxT4UIh1ZBm/wpjF3" +
1087         "3LBJOObLDA/+qnI0iNooOiFa7nQrG6TbWxMWtXNfw66M0sA+PbDL8OyLhgvCwUQYWmOo1Q" +
1088         "IDAQAB",
1089
1090         // C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
1091         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2g7mmY3Oo+NPin778YuDJWvqSB" +
1092         "/xKrC5lREEvfBj0eJnZs8c3c8bSCvujYmOmq8pgGWr6cctEsurHExwB6E9CjDNFY1P+N3U" +
1093         "jFAVHO9Q7sQu9/zpUvKRfeBt1TUwjl5Dc/JB6dVq47KJOlY5OG8GPIhpWypNxadUuGyJzJ" +
1094         "v5PMrl/Yn1EjySeJbW3HRuk0Rh0Y3HRrJ1DoboGYrVbWzVeBaVounICjjr8iQTT3NUkxOF" +
1095         "Ohu8HjS1iwWMuXeLsdsfIJGrCVNukM57N3S5cEeRIlFjFnmusa5BJgjIGSvRRqpI1mQq14" +
1096         "M0/ywqwWwZQ0oHhefTfPYhaO/q8lKff5OQzwIDAQAB",
1097
1098         // CN=GTE CyberTrust Global Root
1099         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9p" +
1100         "TAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6" +
1101         "XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQ" +
1102         "IDAQAB",
1103
1104         // CN=GTE CyberTrust Root
1105         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6jr11kBL65Xl0stn3JtQOQR3pNgdWct" +
1106         "W4adpU1LHWeG2q4zs9o4Q3JcevrwTcsyKx6W2+gm3rjS+9tK5wHqLWbiAxUeZWXHNSsiNQ" +
1107         "Trz7mmdAxIYRRsdDIrrqAE9scs1hnN7L+u4w0ub6W53Fmdwg+Dm/ZIwHVju93Gxe9r/h2Q" +
1108         "IDAQAB",
1109
1110         // C=US, O=GTE Corporation, CN=GTE CyberTrust Root
1111         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC45k+625h8cXyvRLfTD0bZZOWTwUKOx7" +
1112         "pJjTUteueLveUFMVnGsS8KDPufpz+iCWaEVh43KRuH6X4MypqfpX/1FZSj1aJGgthoTNE3" +
1113         "FQZor734sLPwKfWVWgkWYXcKIiXUT0Wqx73llt/51KiOQswkwB6RJ0q1bQaAYznEol44Aw" +
1114         "IDAQAB",
1115
1116         // OU=ValiCert Class 3 Policy Validation Authority
1117         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFl" +
1118         "LWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2" +
1119         "bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPw" +
1120         "IDAQAB",
1121
1122         // OU=ValiCert Class 1 Policy Validation Authority
1123         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSL" +
1124         "wxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+Fiw" +
1125         "nRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQ" +
1126         "IDAQAB",
1127
1128         // OU=ValiCert Class 2 Policy Validation Authority
1129         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZU" +
1130         "cOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XB" +
1131         "hVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9w" +
1132         "IDAQAB",
1133
1134         // C=hk, O=C&W HKT SecureNet CA Class A
1135         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtBuiCqVMc2NGUUh0Y6i0jBbb9M" +
1136         "hn3qFIAv/Lo8+n39mxMeDjLihxBKZkWsZc/tCnuOo+Ctr7EX9/JCheyIqsbniqyKIYOZ5M" +
1137         "UNHwmLXvpLIbYGu/+XO0C3X5Irvp5YGgldJ2THzTp/5dlRXtB9TH3mAwAO7yLpTxhjLlWV" +
1138         "Ho34CiKgDvPIhdEeMAX1TkDEcQbLD1+DN2HDRmW9S7NGM502aUOuzNIinz9hK71CEpN6VE" +
1139         "Td+JDAQMfUF7h/MWwUMpZLTWRWerhkxljwG36mOMTnhUREcaU4aMaxgnIQvFVmYOJfbgea" +
1140         "xoAHTpmmQ8SU6e4B3IiBtQBvddCfiNixP9XQIDAQAB",
1141
1142         // CN=IPS SERVIDORES
1143         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsT1J0nznqjtwlxLyYXZhkJAk8IbPMGb" +
1144         "WOlI6H0fg3PqHILVikgDVboXVsHUUMH2Fjal5vmwpMwci4YSM1gf/+rHhwLWjhOgeYlQJU" +
1145         "3c0jt4BT18g3RXIGJBK6E2Ehim51KODFDzT9NthFf+G4Nu+z4cYgjui0OLzhPvYR3oydAQ" +
1146         "IDAQAB",
1147
1148         // CN=Microsoft Root Authority
1149         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQK9wXDmO/JOGyifl3heMOqiqY" +
1150         "0lX/j+lUyjt/6doiA+fFGim6KPYDJr0UJkee6sdslU2vLrnIYcj5+EZrPFa3piI9YdPN4P" +
1151         "AZLolsS/LWaammgmmdA6LL8MtVgmwUbnCj44liypKDmo7EmDQuOED7uabFVhrIJ8oWAtd0" +
1152         "zpmbRkO5pQHDEIJBSfqeeRKxjmPZhjFGBYBWWfHTdSh/en75QCxhvTv1VFs4mAvzrsVJRO" +
1153         "rv2nem10Tq8YzJYJKCEAV5BgaTe7SxIHPFb/W/ukZgoIptKBVlfvtjteFoF3BNr2vq6Alf" +
1154         "6wzX/WpxpyXDzKvPAIoyIwswaFybMgdxOF3wIDAQAB",
1155
1156         // CN=Microsoft Root Certificate Authority
1157         "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA8136gGfUWqepDCyQINA1CDx1hM" +
1158         "23B4mcidrezsNg+pFoWp6UcSkYdnzC4MgldpQOWPoENDbm36/3gLrpWAsrk+WdBeN3IpH3" +
1159         "NGQ8IpEdXuEJkLwU/vx1WBnhebcHkqOuiFkI2J8HygNY/GgpbTLX0qjLS/zhC0gyT+bruK" +
1160         "1P5FxvE5SZ25XVdduoGreUkbR3W/VIDI9qeX0UcAR9ba+Q9dpw2Ee3v5svbOcFt+ERYKx5" +
1161         "kRR8xdam5OF+1cN+5ZLSPAC1NoLeeeFt87Vu+J8zyctSfXOYNtuLoWuilZebo97CTSb/Bp" +
1162         "ZnJQbI56zk7hIzlTGZyDUITjTKeVPVtb5jMllANsClTgRNPdtbBzPkWL/vP1Nk2EJZNVf9" +
1163         "D0V8JARNntY4dBGXIpDOaER0km/VS2+whuPHNkKg0PzBwFr5o2G5MEdxlgoWsJHAQpXvEH" +
1164         "8oauMqH7HkzQM/d3EExyD8SQ8dRYik18t+iK2OLexF28RRBMkq/OyGnpoRl1vezlOI5uK3" +
1165         "/ayVwihA2+8EkN+BMznZskWlI4cGpVWJMbsGLWAOQRh9Hy61l8sR6xXVJKWU7xUUif1Lc/" +
1166         "oyW/zRMwD5WWJwBzLqLqtALXvK3SFnGzCZjxaqI6hB0bBuEZs2xN5AdJzhWGXBYB56WzjI" +
1167         "j7sEJnzUFkDltmtsqob9AL/OwTUCAwEAAQ==",
1168
1169         // CN=NetLock Expressz (Class C) Tanusitvanykiado
1170         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDr7LBsYYojJa9gIOPZn/yTC9tdjbChs0" +
1171         "A6gs79deB4MgOGWoaVke1T+p1A/Obo3dlbegO9XfM7DMNReZutVaDp0AMQrwq6FELZUiYR" +
1172         "IsfSIMyCpJqp/riBdp1qt9I2dT6xhgn2bm1+Trd67K5xhPYEMwglMut0rBZExuRAkx1/rQ" +
1173         "IDAQAB",
1174
1175         // CN=NetLock Kozjegyzoi (Class A) Tanusitvanykiado
1176         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeL" +
1177         "Vu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX" +
1178         "9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8Wg" +
1179         "D/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7tqyF" +
1180         "/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCo" +
1181         "R64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQAB",
1182
1183         // OU=Tanusitvanykiadok, CN=NetLock Uzleti (Class B) Tanusitvanykiado
1184         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBN" +
1185         "wcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX" +
1186         "iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvcQ7GhaQ" +
1187         "IDAQAB",
1188
1189         // CN=Post.Trust Root CA
1190         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1n8T5A0k2Nj76bbDsVKjTty3O+" +
1191         "L3Dl+B5gHwpuY2cNgTc6H/UgiQ8hW88jIcqNfhBhB7QaiUxz89RBXcgFHnMP5TSPWQX21t" +
1192         "JeBgu6D71sYp+E1wUBo3oA7NeCq2aPOZ1AyOXhJi/8JfWporiEequ6HZdfAsXP5twrFbMc" +
1193         "yDhxqnvpAO6BBUU1ILnEnzgAL+byemo1cwuNu40AAEA+Tl1EMG66toTWgm0pk0ueASln9L" +
1194         "u2tuIXHmCEVKHWYNN8kD4dHK3LEvcPa3gWKWG2Sn/rvhhutBn6ic2Mqg4dYv+A/hukA492" +
1195         "3RpcpMGciW3MxJHAq206iROvna7B3Nc0okPwIDAQAB",
1196
1197         // CN=PTT Post Root CA, 0.9.2342.19200300.100.1.3=ca@ptt-post.nl
1198         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsH7iOgHxSK1T1HHO276A4FCtma" +
1199         "KEeto6JyQ6EYE2Eg3mo5mOpMwmtQ5hxu4oq22G3y6XYfpAacmNjMQxe/pSXlZMIJ5gGl9s" +
1200         "SnjJiTyflYasd2cOpg5C6CxiSTJLBD4yQ5AOCiLKyHQOhe+DgcVb8ttshQhvTialBqt245" +
1201         "iiTl7EgODo+8zpMGzycmGuJ35T1BWUD9KPeYLZ9o+rxhPmHJh0SwBhDnlpVPKQsqMJAWX3" +
1202         "BEdsTvopK/AOBheT3ILAEd6PsDBGWUhKZs42r8fPMdGSdBQj1aq64InbEtHs1GkjuAsWST" +
1203         "POGvninF98aB13uwGqZ+Ixxv/WOmn9DBt8IwIDAQAB",
1204
1205         // CN=Saunalahden Serveri CA, EmailAddress=gold-certs@saunalahti.fi
1206         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5wQp3NbgUtPWTwCvHIGIvzxUcv" +
1207         "OeeWP9y2DaDHxyL8obqeIQaWd6OZ/CoCXMg4ONgxEcuP3n26mIowySIVfBquLqM35KZgO8" +
1208         "c43SHCn9x39D7Y/rV3uhQb9NczFKNyi0GFdYPGhwUJO6EB14zZPDwoLvuN8PDFjVMFdDOh" +
1209         "QlKjhZBrREzdvJXkbyS7gcQ0GB0j5Dsq4hnhtKgHymyrP0JqkuLPi39zwYD5sybxEJc8TN" +
1210         "L+jT7Ek284GN2ML/0Bpt3dgUvzLQ6cMNPgiv7dpLnWrPE4uQgmn612cjYUtb/aWAZB1696" +
1211         "XT2ncceLtR++dGgJBxcbYW+EO0Gb0Yq952ewIDAQAB",
1212
1213         // CN=Saunalahden Serveri CA, EmailAddress=silver-certs@saunalahti.fi
1214         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0neMvIdsNk5TqmhgRbgjd2fj7k" +
1215         "mC5mx/XpJdtRxhUteoYEsW+ut5fp1MkulXe16GMKkoPH030SHidhoZw++q2u74AZ4aOSov" +
1216         "k3UDZj9uKU2NhGOMpx8VlLQ0SbTk00GruvvEXLWecvUoyjKCY0zHRPi0HcSKCldVkK8wiV" +
1217         "QOp2gm00AHIrPOPKP7mNckPN58gkm0NIx9JNtkbmSy6f+GyKx+q1Pk0kH0EYTuR0wIHUTm" +
1218         "Vk0AfNqJQjnveAjRhea+XJ4zuTX/HM70g7XyZMUxSKm0rMXYPIwabab/Qq3z+EvOrNrFir" +
1219         "APAyPB9fPHWX8w8d9mHVoxBaJGHTnkVbOtDwIDAQAB",
1220
1221         // C=hk, O=C&W HKT SecureNet CA Class B
1222         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn+AlkQ8EV8LHXLFlAmYPqP3YMQ" +
1223         "5vgmz5wx6w46C9OERSx4x2EnhMfsIrjIrk+dwK4JVF3+seftJE+AMVAOzEsTx6tk22lgp3" +
1224         "vAdg7/C3N/6J/bLYB6tS/oI/vDVnM9n7LNy1WGGiDLF9lNGohGkkPZfNmwhMUImBmh/Swi" +
1225         "BvzD8OZcThSEncO/nlKjEHbqZrR6gZWq7ToXS1vMLbOT36q7DwySIJ1DxGaGwuLh/4qIwR" +
1226         "oXY1UpLXq4gh3L3pnNn4Pt4wMUwCIi9XZrtWcjk3UJmvV9D0S9Qp7alvxtOyhpGLHRBtaB" +
1227         "Zk8Q5tv15n/bKOcGXnb3K8RHWrAXb/N2RFIQIDAQAB",
1228
1229         // C=US, O=RSA Data Security, Inc., OU=Secure Server Certification Authority
1230         "MIGbMA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6z" +
1231         "V4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXAT" +
1232         "cXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAA" +
1233         "E=",
1234
1235         // C=au, O=SecureNet CA Class A
1236         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqaN8+JCzjoRM4Aq+qxIqjQw+L7" +
1237         "XdmxCIuWq3h3Ugt0vvIiMG6/BWMvfLLXDFA2+3wdDDZhMCvVVJh4fpLZ6l5XY2q+JkJViI" +
1238         "wxsbAvBdsY+fE03CUim0EDVPNoivCy2BCCRhw2iNWm0x6FQZUxf9pxP2QJmmqCnAn0J7Jy" +
1239         "nB7tvvjQNkJYGx/pUaHtoQQWIbVn8YGEiY0k1LwRhot2lna2RMbo8CvxRpe/ZEIxDpLrxe" +
1240         "Ys1bnMyjjoxRgbSiorG8qMnoKpiqu0sVoeHpkHqef+hlBegRcXpv43XeVT/L2OrIAM0llH" +
1241         "JkHu99ED5NL5F5vQLq15DBSWhuWRQl4t3dCQIDAQAB",
1242
1243         // C=au, O=SecureNet CA Class B
1244         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApmPZxhVadudGZcc0kfl73Va7+J" +
1245         "Y1LinKp30KHvcxUuhayNPPOQFOW/AfsbhK0rNHQ2Y/AUBOMEnhD/3rEmN4zPYWYhj1b2n9" +
1246         "fm4zdiGjwIgP6uYl/KmXzBhyxzG2C5vNwsV4YWNFrDSmJ3hoxL1SaM6ETdIkpShsgObK5s" +
1247         "/mmp5QeM7zNtKjQ1ocBq/LIO7QLMREGJBssZFkZbm3hYNLqJGZxeCc97hQ19OwT5rtY/tN" +
1248         "9NQoJDqAW3uTjMUFhK87hv6BMce2nV8a6pB7sEZesghSAFcNVVKDeJVK/WiPntlQtktT+v" +
1249         "KFApVOOPWDp5bUMT8/p8o3U9zFL20adKbMvwIDAQAB",
1250
1251         // C=au, O=SecureNet CA Root
1252         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApyi02Dz1v3oGkb2lQkyzfJ6IZp" +
1253         "nF2xfURVTDe8DwJFZmmL9E4HkTdmiu3Zp0z6Lpl+bBwKnD9yzVNjtzna+C2twOX1Ov625Q" +
1254         "16jwqo6rY9Kbdf5VCnzRs8BZk1Eqh2mKGe3k19eOFKu1GVizzmzgTYLTA4TBqwAYekmoFX" +
1255         "0IyQFgJ5To+wlgntE/Ts0To3j9ZfcRX/abADCMIu0oiWUb0x9he8Mjo+PGgPmD8/e63oZ4" +
1256         "X/aVw4xqSCJlhdMiefb9RBboD2EENip1xtviZRQnYtyCXJYSMw5MGNX2PJ2xzWEcsYX5A9" +
1257         "G69kzW7p990ZIh8PYKFqQ0h/dWj5O+l69SpwIDAQAB",
1258
1259         // C=au, O=SecureNet CA SGC Root
1260         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1uxDYpTIbpSiDiQQmVE/Vbrc8" +
1261         "WF8wYx5Qj8jLHVescLIwq8WgkiAfinwN5XdDGLrTbMXnP39kTwMcr1LKIF8wocMHqGM+JG" +
1262         "U/Zk1kersVOUY3fEYtMvC+pfsHUCXvgrzybz3tKt62V/vC5BhPyZmumBG6ecZsf49bKEGy" +
1263         "B1ciHHhP8CRswPpmmFfVkh1Q6nXVYVT8wfQSx/Zhuv691Bo+yp5lZK/h6nxFwiny/gC3QB" +
1264         "cMhzgwoHpGie5FEOjXQxL6LG2ggQK+8lPmyGtUbnl4PAq96wrgYa58j7736tjrCaRfGb9b" +
1265         "HoMbtkAL9/kWbNqK+V6hM6Akxb68CT5EH8rQIDAQAB",
1266
1267         // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA1
1268         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlJAMS3EpHNr2aHl6pLrn0syNr+" +
1269         "hHkJkfxirql2PoH84XV8Yas6jHfIftNTWAurpubb4X/swtG2zvigBJFuHuBl5KB12rPdFQ" +
1270         "uJFG1NTaFdiUXA7K19q/oPdJPMi7zuomgQoULZwNN0VrQcpXizjwJh8x/M80jo93wT/jq1" +
1271         "Q8J7TOMkxVE2L8/joWJc8ba6Ijt+DqAmm79yJxbXwLGZOhl5zjkWkfaOQvfRBtj2euwRCi" +
1272         "sF5jSpf35niprSa7VMnftO7FntMl3RNoU/mP6Ozl3oHWeD7uUEC0ATysFcGCOy5/8VIni3" +
1273         "Lg59v5iynDw0orM4mrXCoH/HwjHitPCCL+wQIDAQAB",
1274
1275         // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA1
1276         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlJAMS3EpHNr2aHl6pLrn0syNr+" +
1277         "hHkJkfxirql2PoH84XV8Yas6jHfIftNTWAurpubb4X/swtG2zvigBJFuHuBl5KB12rPdFQ" +
1278         "uJFG1NTaFdiUXA7K19q/oPdJPMi7zuomgQoULZwNN0VrQcpXizjwJh8x/M80jo93wT/jq1" +
1279         "Q8J7TOMkxVE2L8/joWJc8ba6Ijt+DqAmm79yJxbXwLGZOhl5zjkWkfaOQvfRBtj2euwRCi" +
1280         "sF5jSpf35niprSa7VMnftO7FntMl3RNoU/mP6Ozl3oHWeD7uUEC0ATysFcGCOy5/8VIni3" +
1281         "Lg59v5iynDw0orM4mrXCoH/HwjHitPCCL+wQIDAQAB",
1282
1283         // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA2
1284         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlnuSIz9g3wk8WIAI42MJl+jkC3" +
1285         "Vh1M0Oo/LjHkO6g/+6gVwvyN6Qi0wOLyn5B9aOs6Yor4Iqe8K0Zkxx9Ax0GrjbGuhoN6n5" +
1286         "oaJuHCjNbCY8jyoznp3LtHnE2WQ9lcYzqEf75QcJ3PZtuCVCTMP7Su1bLtQHqOWTECSTWG" +
1287         "59wdAez+kp19C8X0zwFRbD2MLO41sXW5SLKGsUZyQ79FLsDW58TrSZAtvJ8w+CqwH0jN4W" +
1288         "cMa8Fwdh/xFAhOosG3o6sANhB6qWjdDauYOO5J1RaXVxZIG0iFXcEIPOLaX1MJZhLjsK/I" +
1289         "dfnFyCdRMe05jR7cntchYcDAbcWSB+8F3v9wIDAQAB",
1290
1291         // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA2
1292         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlnuSIz9g3wk8WIAI42MJl+jkC3" +
1293         "Vh1M0Oo/LjHkO6g/+6gVwvyN6Qi0wOLyn5B9aOs6Yor4Iqe8K0Zkxx9Ax0GrjbGuhoN6n5" +
1294         "oaJuHCjNbCY8jyoznp3LtHnE2WQ9lcYzqEf75QcJ3PZtuCVCTMP7Su1bLtQHqOWTECSTWG" +
1295         "59wdAez+kp19C8X0zwFRbD2MLO41sXW5SLKGsUZyQ79FLsDW58TrSZAtvJ8w+CqwH0jN4W" +
1296         "cMa8Fwdh/xFAhOosG3o6sANhB6qWjdDauYOO5J1RaXVxZIG0iFXcEIPOLaX1MJZhLjsK/I" +
1297         "dfnFyCdRMe05jR7cntchYcDAbcWSB+8F3v9wIDAQAB",
1298
1299         // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA3
1300         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmV4egJZmI2TOnIwAPgqvCOm4BO" +
1301         "CEuG1TdU02qLXg14xOYFW2A5ebWhqn87o92ZqUMXZ0I8n37BJd2CDUHekbojd2BA8+rBZp" +
1302         "O+H/EC9WJeQzUBMJzE4Oq/Dkddtx1fxKze3bDzUFFdWwZntCeyblWeK1x8Cyx6FD/Q8vC4" +
1303         "MlJVeBu7vRNTB0kZCyj59o1dJDt7JFqSPAVtiHEtNz/stZ6q/85x9eVEUcqm2Vk2JHQkFe" +
1304         "T+s2Bw4oeFQKfMDDJBOGAwK5rHaSSlrdxdzs+LPbK7UbNud4gkyVfiBWsnUcfZfvf5Q4Ka" +
1305         "IA4tHqseM0NjFAWLiqt86BGgwXgQ3967jTvQIDAQAB",
1306
1307         // C=hk, O=C&W HKT SecureNet CA Root
1308         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtBiikFaM1l2/RliRJ+qddeCk66" +
1309         "JQcIdFSUmSa7c5AEt7qNpA4eYNouA3AUhNznLhXJPTw/mSDSTvSM5HKsutkjqq1pWy8hme" +
1310         "PpV8j2ACdJMWKGn+O+5deJMcejwj6WE5bMUwLR+EkgVx53TBQkfpMLGjFww2Y89Q0DKoh6" +
1311         "VAYhQROPvOL40zsIvpjnD7sJ7HXQPu9uWNcjzIvFSSz8qQ38jbrwXx61DK0QWsBbQBFZb1" +
1312         "6zihafeDQ+g8pl2lLLokFi/7DjJwphLWmTb3axuj5/zHG8jYL3XRNbPpwtaPBB3BtX4EOz" +
1313         "iJ5KMj8P3KvczrnRcGFXLt0Ob71m+z8Z0+uwIDAQAB",
1314
1315         // C=JP, O=Japan Certification Services, Inc., CN=SecureSign RootCA3
1316         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmV4egJZmI2TOnIwAPgqvCOm4BO" +
1317         "CEuG1TdU02qLXg14xOYFW2A5ebWhqn87o92ZqUMXZ0I8n37BJd2CDUHekbojd2BA8+rBZp" +
1318         "O+H/EC9WJeQzUBMJzE4Oq/Dkddtx1fxKze3bDzUFFdWwZntCeyblWeK1x8Cyx6FD/Q8vC4" +
1319         "MlJVeBu7vRNTB0kZCyj59o1dJDt7JFqSPAVtiHEtNz/stZ6q/85x9eVEUcqm2Vk2JHQkFe" +
1320         "T+s2Bw4oeFQKfMDDJBOGAwK5rHaSSlrdxdzs+LPbK7UbNud4gkyVfiBWsnUcfZfvf5Q4Ka" +
1321         "IA4tHqseM0NjFAWLiqt86BGgwXgQ3967jTvQIDAQAB",
1322         
1323         // CN=SERVICIOS DE CERTIFICACION - A.N.C.
1324         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsiov7CtZakOTiUYqiuXs+gX64s" +
1325         "jeQWuvA9sAWu9IN89XifvdyZIQ3ncDlRyQPse2ZyU7VZjv2Tz+JuSKO0SpdDeDCncndLip" +
1326         "ca3dlxPSyqIuuLqdyb5Z6Nly8oqFZhxHXrSHgtYP32cmpr02sfNdkFBRdjIsOy+qX2Fe41" +
1327         "TVEl3/DY0Rx4J6Nt/hTBbEdN0tau/QsfAzp/6/N2dDEi55SpSvhPsHEQhOMJN16QFUzsXe" +
1328         "FIbwrq6bciUPRHfi82yveZwuSceemHYyFpq8AN7gtCAFkRfdgBUU7jZBxCGP7tkAShnGcW" +
1329         "GlEV0AO+SndGw6Sm6D4HoxXCFl+AiHQodn5QIDAQAB",
1330
1331         // CN=SIA Secure Client CA
1332         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDS/LBAYGpmY1Jm5mkJcY2BmB4dHfPgSQ" +
1333         "3IK2/Qd1FFxZ1uo1xw3hV4Fh5f4MJi9H0yQ3cI19/S9X83glLGfpOd8U1naMIvwiWIHXHm" +
1334         "2ArQeORRQjlVBvOAYv6WpW3FRsdB5QASm2bB4o2VPtXHDFj3yGCknHhxlYzeegm/HNX8ow" +
1335         "IDAQAB",
1336
1337         // C=IT, O=SIA S.p.A., L=Milano, CN=SIA Secure Server CA
1338         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA28ELzCfTEiIuuWQWdKxZJ+IqkA" +
1339         "CSntWYXCtRbhsTb1RvShCihethC+ztnH7Of2WTbsxsQZzILarGs5v7THCcEXXzcom6iQCt" +
1340         "xy5J53PagLIs/vKXmfQCGzQvOaqL5u8F/Ln1ulR/ob+OHkg2Mwl0Yac9x5skx8OJzcpOKD" +
1341         "EjBhxiFY7fTxtrLUri9LDczvOQ/XmBE8E+Lma8+SJNCy9iM42oK+rpb3OnN5QEL+leTQ3p" +
1342         "7XwyP3lK5jp2KSBQ84+CRHJsMDRIWKpdGz8B6yHs6n6oK4Rd9sExlU8pe7U1t/60BlewFN" +
1343         "fyVVmMupu5MT/lqqrvJXCVkjZB8VWfwQhEnQIDAQAB",
1344
1345         // OU=Public CA Services
1346         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOeC2xUTrnnCtF+SjyO8uvfG0Q" +
1347         "Cv1lRp8V2mYvhh0Zzeyjss6VwWJzTmuNHKdO8leGRt/hzoiXMxU2dnhsStamjnClZEgzpY" +
1348         "R4l3Gtpv8vkHQMk9Ae9q0dlrhJ7FaytOtyz4pGpXq2gxuhlmuuwbV/vOStZLeMPBgT1Llj" +
1349         "CZqcMt4uQSJgqkYxIc1HfIgdSnVUMt/ARWndwLrrdsCtozkIgFyX5UgujSMtDXAUkqNZB5" +
1350         "OXPWi7xhzYdtUBUFTKnoSkcxiwXM5flC1xJg+Do/o6k2GqWGNiymBIMJ9lLFsH0fiEGQmM" +
1351         "VlaJYQshPJFkm9Kr6wSKfC/S1eVtA3TVhR+wIDAQAB",
1352
1353         // OU=TC TrustCenter Class 1 CA
1354         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwKeu0drOu17ZbtF7nveOxnEkEV1uhq9" +
1355         "l/Exv9umGr2Odx3y0AlF1RSH0j73VihJA8Ch9ZEXQvjoCl/TACPSlSzXIaSSGcvMtSjkih" +
1356         "Y5bIEIUwaVd0RcBahsbVPeBoV30xaiSNRZc+MX5oZjJuJG3sMjbJQcrwMUTIo2HKG6A2Hw" +
1357         "IDAQAB",
1358
1359         // OU=TC TrustCenter Class 2 CA
1360         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaOOjtMgApcYMBDb+MAdzaxq05pKmKL9" +
1361         "WLXGhfUMZi9Wa9ypEi7KodUdc9s1Gyg05dy0mw8ExV5Wstx4ULMBySToLUygLt92++3ODj" +
1362         "FLgFU/Ka9FaLWp6Fk9G0glauTbuoS1cWvP74WJ74KY2we814yU+si2cM8Zz7/FebV1xPDQ" +
1363         "IDAQAB",
1364
1365         // OU=TC TrustCenter Class 3 CA
1366         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2tME1BS4NjeygQGocDiemUJJrUBsH3i" +
1367         "7ndszg2vyEqF6MY2orTdlOAnYRwQvyjXnKALbxsA7X+6QXPa+raXqWJ7+vM6GaKlmqxLU3" +
1368         "CPISpTG2Q/UylnEoKKuNKIbfu+7jDH0w1sNSq49dJ5xrwKPnBWtXSUSzbupkz9KOelB3dw" +
1369         "IDAQAB",
1370
1371         // OU=TC TrustCenter Class 4 CA
1372         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/L2PWNnuyDdNV9WRs5iVdxrTIFLolOI" +
1373         "PrVmKlVallo/QjmcJLudDNVGemo6CjqTMrduS9rXey7VwSdMPFtg9SmnKTQ5BiZhUPRaXd" +
1374         "4N24b0BuV8F5cqNgqrp2HRKJU1r8Ar7hCRPFSi/cPYsZrdeLJEX7TPTNXDUdKUxR8/JsVQ" +
1375         "IDAQAB",
1376
1377         // CN=Thawte Premium Server CA
1378         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSNjZqi9fCW57agUFijzjuSQRV1tDvHB" +
1379         "uVFkfvGEg1OlL0K2oGjzsv6lbjr4aNnhf3nrRldQJN78sJoiFR2JvQZ9C6DZIGFHPUk8uX" +
1380         "KgCcXE4MvPoVUvzyRG7aEUpuCJ8vLeP5qjqGc7ZGU1jIiQW9gxG4cz+qB430Qk3nQJ0cNw" +
1381         "IDAQAB",
1382
1383         // C=hk, O=C&W HKT SecureNet CA SGC Root
1384         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqFNPj0Pdr+zBtA0bX7cIoprIQu" +
1385         "Nt1yUa3+DKvC8iJPlpIr0arVHncfe1dtTzPsg+EdBNe5keGLeezT5hG0URS1sm3Ck8AE0R" +
1386         "2h2Pnh903hVAvDDJD9/4LXzYjZ2g4J+wzydgzzgRCO82L3xONh0mAqf01FBDgUnr3beWFD" +
1387         "BjMtEDzSG8N5EePmWuFoL2FWBLUTuW5RnowvemBYE6qH8YWD53w1kAg/T1eUlgpy4DPgH9" +
1388         "heLfoZqJ2fhkCiuEzUPNJTUAXjBmdKHHCHWsSSeC17CVNW4dmYDrkqAtWtY4u7VHJ6sazL" +
1389         "9TU8FGsm/o101XEd2wNUgfqybqVg24CjC22wIDAQAB",
1390
1391         // CN=Thawte Server CA
1392         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTpFBuyP9Wa+bPXbbqDGh1R6KqwtqEJf" +
1393         "yo9EdR2oW1IHSUhh4PdcnpCGH1Bm0wbhUZAulSwGLbTZme4moMRDjN/r7jZAlwxf6xaym2" +
1394         "L0nIO9QnBCUQly/nkG3AKEKZ10xD3sP1IW1Un13DWOHA5NlbsLjctHvfNjrCtWYiEtaHDQ" +
1395         "IDAQAB",
1396
1397         // CN=UTN - DATACorp SGC
1398         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLi" +
1399         "t6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZD0/W" +
1400         "w5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK4ESGoE1O1k" +
1401         "duSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykqlXvY8qdOD1R8oQ2A" +
1402         "swkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv33i+Ybqypa4ETLyorG" +
1403         "kVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB",
1404
1405         // CN=UTN-USERFirst-Hardware
1406         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsffDOD+0qH/POYJRZ9Btn9L/WP" +
1407         "PnnyvsDYlUmbk4mRb34CF5SMK7YXQSlh08anLVPBBnOjntKxPNZuuVCTOkbJex6MbswXV5" +
1408         "nEZejavQav25KlUXEFSzGfCa9vGxXbanbfvgcRdrooj7AN/+GjF3DJoBerEy4ysBBzhuw6" +
1409         "VeI7xFm3tQwckwj9vlK3rTW/szQB6g1ZgXvIuHw4nTXaCOsqqq9o5piAbF+okh8widaS4J" +
1410         "M5spDUYPjMxJNLBpUb35Bs1orWZMvD6sYb0KiA7I3z3ufARMnQpea5HW7sftKI2rTYeJc9" +
1411         "BupNAeFosU4XZEA39jrOTNSZzFkvSrMqFIWwIDAQAB",
1412
1413         // CN=UTN-USERFirst-Network Applications
1414         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/uRoeQ2VYWsBjRboJpYsvi1Dw" +
1415         "V3g64ysXaSaOwjSsl2P+Octjd5A7mraY0HJbYZZ+SwGxhzYUrofs3TL2TjpnwM+heAow1H" +
1416         "iU9RcS/u/D/5uBaAh4mTJSCaQ4JpJHYoWTWhHcB/gwZkFiAs00mkhbTAYX9RCPhoFZGAy6" +
1417         "XV7js69IQEXmBZp4w0cu64eMXROxJKb35lJ7mkVcW5b0OkxR0smcBSpHhMFbNAmAhrQ8YB" +
1418         "sHp79WscIj/L7/+o0DpLdhWe0tHGLuPbVxsyorhv6IamP3Cr5XCSq0QeQFD7nKNi5GxuoM" +
1419         "je4oBC+ukv6M4yBI98jbccozU8Fd2ew66XpQIDAQAB",
1420
1421         // CN=VeriSign Class 3 Public Primary Certification Authority - G3
1422         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy7qcUvx4Hxoebxs3c734yWuUEj" +
1423         "BP8DZH9dCRCvUXyKVhwRZATfuKYZDldiDBEQZ9qyxupvURQY76La0qYVmkZyZM0Oi8Ultw" +
1424         "IARY0XrJpGm8gxdkrQWLvNBYzo2M9evwQkkLnZcnZzJu4a6TFRxwvCBNLxjekojobIVXER" +
1425         "rpfuMmEVSiRZZVg8owiejc2KPtKoA/f3llVz4VIGYIL5WTv6pHL6hGl/AS4v7CCitR5nbm" +
1426         "t0a34g2mzKjDTFlVieboU1wc6p3wYhYLp8lfDPDewnbOr/dq8vpBpqIzFMnlemPTnmI31Y" +
1427         "Vlng7mUyR0G14dElNbxyzng0k7Fa6KaLlXlwIDAQAB",
1428
1429         // CN=VeriSign Class 4 Public Primary Certification Authority - G3
1430         "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArculEWnGWavxj7UZD1bOzLUfIO" +
1431         "SeJiVL4HNliVne0IPk9Q+1u63xfOgh/OToDO58RSIZdpK0E7cgWwn6Ya6o8qWNhcIq1t5m" +
1432         "NtKbAvSokmB8nGm0jyQe0IZS9jKcQVgeIr3NRWKVCG7QZt1ToszwENxUc4sEoUYzM1wXQL" +
1433         "meTdPzvlWD6LGJjlp8mpYikDuIJfLSU4gCDAt48uY3F0swRgfkgG2m2JYu6Cz4EbM4DWam" +
1434         "m+rJI1vbjuLzE44aWS2qAvDspIdm3ME/9di59OyCxtI9lR3lwE+EydmjRCgGatdFrPBrau" +
1435         "9OX/gRgh44YzRmUNQ+k3P6MMNmrf+TLZfvAwIDAQAB",
1436
1437         // OU=VeriSign Trust Network
1438         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRN" +
1439         "zjMHPVKmIquNDMHO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDH" +
1440         "qGKB3FtKqsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHw" +
1441         "IDAQAB",
1442
1443         // OU=VeriSign Trust Network
1444         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRN" +
1445         "zjMHPVKmIquNDMHO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDH" +
1446         "qGKB3FtKqsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHw" +
1447         "IDAQAB",
1448
1449         // OU=VeriSign Trust Network
1450         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm" +
1451         "1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71" +
1452         "lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZw" +
1453         "IDAQAB",
1454
1455         // OU=VeriSign Trust Network
1456         "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm" +
1457         "1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71" +
1458         "lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZw" +
1459         "IDAQAB"
1460
1461     };
1462
1463     static class entropySpinner extends Thread {
1464         volatile boolean stop = false;
1465         byte counter = 0;
1466         entropySpinner() { start(); }
1467         public void run() { while (!stop) counter++; }
1468     }
1469     
1470     static { 
1471
1472         entropySpinner[] spinners = new entropySpinner[10];
1473         for(int i=0; i<spinners.length; i++) spinners[i] = new entropySpinner();
1474
1475         for(int i=0; i<pad1.length; i++) pad1[i] = (byte)0x36;
1476         for(int i=0; i<pad2.length; i++) pad2[i] = (byte)0x5C;
1477         for(int i=0; i<pad1_sha.length; i++) pad1_sha[i] = (byte)0x36;
1478         for(int i=0; i<pad2_sha.length; i++) pad2_sha[i] = (byte)0x5C;
1479
1480         try { 
1481             if (Log.on) Log.log(TinySSL.class, "reading in trusted root public keys..."); 
1482             trusted_CA_public_keys = new SubjectPublicKeyInfo[base64_encoded_trusted_CA_public_keys.length];
1483             for(int i=0; i<base64_encoded_trusted_CA_public_keys.length; i++) {
1484                 byte[] b = Base64.decode(base64_encoded_trusted_CA_public_keys[i]);
1485                 DERInputStream dIn = new DERInputStream(new ByteArrayInputStream(b)); 
1486                 trusted_CA_public_keys[i] = new SubjectPublicKeyInfo((DERConstructedSequence)dIn.readObject());
1487             }
1488
1489         } catch (Exception e) { 
1490             if (Log.on) Log.log(TinySSL.class, e);
1491         } 
1492         
1493         randpool = new byte[10];
1494         try { Thread.sleep(100); } catch (Exception e) { }
1495         for(int i=0; i<spinners.length; i++) {
1496             spinners[i].stop = true;
1497             randpool[i] = spinners[i].counter;
1498         }
1499         
1500         MD5Digest md5 = new MD5Digest();
1501         md5.update(randpool, 0, randpool.length);
1502         intToBytes(System.currentTimeMillis(), randpool, 0, 4); md5.update(randpool, 0, 4);
1503         intToBytes(Runtime.getRuntime().freeMemory(), randpool, 0, 4); md5.update(randpool, 0, 4);
1504         intToBytes(Runtime.getRuntime().totalMemory(), randpool, 0, 4); md5.update(randpool, 0, 4);
1505         intToBytes(System.identityHashCode(TinySSL.class), randpool, 0, 4); md5.update(randpool, 0, 4);
1506         Properties p = System.getProperties();
1507         for(Enumeration e = p.propertyNames(); e.hasMoreElements();) {
1508             String s = (String)e.nextElement();
1509             byte[] b = s.getBytes();
1510             md5.update(b, 0, b.length);
1511             b = p.getProperty(s).getBytes();
1512             md5.update(b, 0, b.length);
1513         }
1514         randpool = new byte[md5.getDigestSize()];
1515         md5.doFinal(randpool, 0);
1516
1517     } 
1518
1519
1520     /**
1521      *  A PKCS1 encoder which uses TinySSL's built-in PRNG instead of java.security.SecureRandom.
1522      *  This code was derived from BouncyCastle's org.bouncycastle.crypto.encoding.PKCS1Encoding.
1523      */
1524     private static class PKCS1 implements AsymmetricBlockCipher {
1525         private static int HEADER_LENGTH = 10;
1526         private AsymmetricBlockCipher engine;
1527         private boolean forEncryption;
1528         private boolean forPrivateKey;
1529         
1530         public PKCS1(AsymmetricBlockCipher cipher) { this.engine = cipher; }   
1531         public AsymmetricBlockCipher getUnderlyingCipher() { return engine; }
1532
1533         public void init(boolean forEncryption, CipherParameters param) {
1534             engine.init(forEncryption, (AsymmetricKeyParameter)param);
1535             this.forPrivateKey = ((AsymmetricKeyParameter)param).isPrivate();
1536             this.forEncryption = forEncryption;
1537         }
1538
1539         public int getInputBlockSize() { return engine.getInputBlockSize() - (forEncryption ? HEADER_LENGTH : 0); }
1540         public int getOutputBlockSize() { return engine.getOutputBlockSize() - (forEncryption ? 0 : HEADER_LENGTH); }
1541
1542         public byte[] processBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
1543             return forEncryption ? encodeBlock(in, inOff, inLen) : decodeBlock(in, inOff, inLen);
1544         }
1545
1546         private byte[] encodeBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
1547             byte[]  block = new byte[engine.getInputBlockSize()];
1548             if (forPrivateKey) {
1549                 block[0] = 0x01;                        // type code 1
1550                 for (int i = 1; i != block.length - inLen - 1; i++)
1551                     block[i] = (byte)0xFF;
1552             } else {
1553                 getRandomBytes(block, 0, block.length);
1554                 block[0] = 0x02;                        // type code 2
1555
1556                 // a zero byte marks the end of the padding, so all
1557                 // the pad bytes must be non-zero.
1558                 for (int i = 1; i != block.length - inLen - 1; i++)
1559                     while (block[i] == 0)
1560                         getRandomBytes(block, i, 1);
1561             }
1562
1563             block[block.length - inLen - 1] = 0x00;       // mark the end of the padding
1564             System.arraycopy(in, inOff, block, block.length - inLen, inLen);
1565             return engine.processBlock(block, 0, block.length);
1566         }
1567
1568         private byte[] decodeBlock(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
1569             byte[]  block = engine.processBlock(in, inOff, inLen);
1570             if (block.length < getOutputBlockSize())
1571                 throw new InvalidCipherTextException("block truncated");
1572             if (block[0] != 1 && block[0] != 2)
1573                 throw new InvalidCipherTextException("unknown block type");
1574
1575             // find and extract the message block.
1576             int start;
1577             for (start = 1; start != block.length; start++)
1578                 if (block[start] == 0)
1579                     break;
1580             start++;           // data should start at the next byte
1581
1582             if (start >= block.length || start < HEADER_LENGTH)
1583                 throw new InvalidCipherTextException("no data in block");
1584
1585             byte[]  result = new byte[block.length - start];
1586             System.arraycopy(block, start, result, 0, result.length);
1587             return result;
1588         }
1589     }
1590
1591 }
1592