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