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