Index: /branches/2.0/security/ExpressoCertLogin/manifest.mf
===================================================================
--- /branches/2.0/security/ExpressoCertLogin/manifest.mf	(revision 1182)
+++ /branches/2.0/security/ExpressoCertLogin/manifest.mf	(revision 1182)
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
Index: /branches/2.0/security/ExpressoCertLogin/src/LoginApplet.java
===================================================================
--- /branches/2.0/security/ExpressoCertLogin/src/LoginApplet.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCertLogin/src/LoginApplet.java	(revision 1182)
@@ -0,0 +1,831 @@
+
+import java.awt.Frame;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.ProviderException;
+
+import javax.net.ssl.SSLHandshakeException;
+import javax.swing.JApplet;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import org.apache.commons.httpclient.HttpException;
+
+import netscape.javascript.JSObject;
+
+import br.gov.serpro.cert.DigitalCertificate;
+import br.gov.serpro.setup.Setup;
+import br.gov.serpro.ui.DialogBuilder;
+
+/**
+ * GUI que realiza o login com certificados A1 e A3 ou login com usuário e senha no expresso.
+ * Esta classe estende a classe JApplet
+ * @author Mário César Kolling - mario.kolling@serpro.gov.br
+ */
+public class LoginApplet extends JApplet{
+
+	/**
+	 * Valor gerado aleatoriamente
+	 */
+
+	//TODO: Alterar a cor e fonte dos labels e das caixas de texto e senha.
+	private static final long serialVersionUID = -6204158613173951516L;
+
+	//private PinCodePanel pinPanel;
+	//private LoginPanel loginPanel;
+	//private PinCodePanel alreadyLoadedPanel;
+	//private int useCertificate = DigitalCertificate.KEYSTORE_DETECTED;
+//	private String uid;
+	private DigitalCertificate dc;
+    private Setup setup;
+
+	/* (non-Javadoc)
+	 * @see java.applet.Applet#init()
+	 */
+	public void init() {
+		super.init();
+		this.setSize(0, 0);
+        this.setup = new Setup(this);
+        this.setup.addLanguageResource("ExpressoCertLoginMessages");
+	}
+
+	/* (non-Javadoc)
+	 * @see java.applet.Applet#start()
+	 */
+	@Override
+	public void start() {
+		super.start();
+
+		int useCertificate = DigitalCertificate.KEYSTORE_NOT_DETECTED;
+		boolean tryAgain = true;
+
+		do
+		{
+
+			// Cria uma instância de DigitalCertificate e a inicializa
+			this.dc = new DigitalCertificate(this.getDocumentBase(), setup);
+			useCertificate = dc.init();
+			String[] returnCode = null;
+
+			try {
+				switch (useCertificate){
+					case DigitalCertificate.KEYSTORE_DETECTED:
+						// Mostra PinNeedeDialog.
+						//tryAgain = false;
+						String pin = DialogBuilder.showPinDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), this.setup);
+						if (pin != null) {
+							dc.openKeyStore(pin.toCharArray());
+							returnCode = dc.getCredentials(pin);
+
+							// se chegou aqui, keystore foi acessada e as credenciais do usuário foram recuperadas
+							//tryAgain = false;
+							assert (returnCode != null): "Valor de returnCode não pode ser nulo";
+
+							//this.dc.destroy();
+
+							// Faz o login
+
+                            if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                                System.out.println("Código de retorno: " + returnCode[0].trim());
+                            }
+
+							if (Integer.parseInt(returnCode[0].trim()) == 0){
+
+								tryAgain = false;
+								// Pega usuário e senha de credentials[1] e credentials[2], respectivamente
+								// adiciona na página e faz o submit.
+								JSObject document = (JSObject) JSObject.getWindow( this ).getMember("document");
+								JSObject loginForm =  (JSObject) document.getMember("flogin");
+								JSObject loginField = (JSObject) loginForm.getMember("user");
+								loginField.setMember("value", returnCode[1].trim());
+
+								JSObject passwdField = (JSObject) loginForm.getMember("passwd");
+								passwdField.setMember("value", returnCode[2].trim());
+
+								loginForm.call("submit", null);
+								Thread.yield();
+
+							}
+							else if (Integer.parseInt(returnCode[0].trim()) == 6){
+
+								tryAgain = false;
+
+								if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                                    System.out.println("Mensagem de retorno: " + returnCode[1].trim());
+                                }
+
+								//JOptionPane.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), credentials[1]);
+								DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), returnCode[1].trim(), this.setup);
+
+
+
+								String redirect = this.getCodeBase().toExternalForm() + "login.php?cd=98&ts=202";
+								try {
+									this.getAppletContext().showDocument(new URL(redirect));
+								} catch (MalformedURLException e) {
+									// TODO Bloco catch gerado automaticamente
+                                    if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                                        e.printStackTrace();
+                                    }
+								}
+							}
+							else {
+								tryAgain = true;
+								dc.destroy();
+								System.gc();
+
+                                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                                    System.out.println("Mensagem de retorno: " + returnCode[1].trim());
+                                }
+
+								// Mostra mensagem de erro para o usuário
+								//JOptionPane.showMessageDialog(this, credentials[1]);
+								DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), returnCode[1].trim(), this.setup);
+								Thread.yield();
+							}
+
+						}
+						else {
+
+							tryAgain = false;
+							String redirect = this.getCodeBase().toExternalForm() + "login.php";
+							try {
+								this.getAppletContext().showDocument(new URL(redirect));
+							} catch (MalformedURLException e) {
+								// TODO Bloco catch gerado automaticamente
+                                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                                    e.printStackTrace();
+                                }
+							}
+						}
+
+						break;
+						/*
+					case DigitalCertificate.KEYSTORE_ALREADY_LOADED:
+						// Mostra PinNotNeededDialog
+						tryAgain = false;
+
+						//Verifica na página se operação anterior foi um logout
+						JSObject document = (JSObject) JSObject.getWindow( this ).getMember("document");
+						JSObject loginForm =  (JSObject) document.getMember("flogin");
+						JSObject loginField = (JSObject) loginForm.getMember("cd");
+						String cdValue = (String) loginField.getMember("value");
+
+						int cd = 0;
+						if (cdValue.length() != 0){
+							cd = Integer.parseInt(cdValue);
+						}
+
+						// Se não foi logout prossegue com o login, senão pára tudo
+						if (cd != 1 && cd != 5){
+
+							DialogBuilder.showPinNotNeededDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this));
+							dc.openKeyStore(null);
+							returnCode = dc.getCredentials("");
+
+							// se chegou aqui, keystore foi acessada e as credenciais do usuário foram recuperadas
+							assert (returnCode != null): "Valor de returnCode não pode ser nulo";
+
+							// Faz o login
+							doLogin(returnCode);
+						}
+						else if (cd == 5){
+							JOptionPane.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+									"Não foi possível realizar o Logon com certificado!");
+							// Redireciona para a tela de login
+							String redirect = this.getCodeBase().toExternalForm() + "login.php";
+							try {
+								this.getAppletContext().showDocument(new URL(redirect));
+							} catch (MalformedURLException e) {
+								// TODO Bloco catch gerado automaticamente
+								e.printStackTrace();
+							}
+
+						}
+
+						//DialogBuilder.showPinNotNeededDialog(findParentframe());
+
+						break;
+						*/
+					default:
+
+						// Mostra Diálogo dizendo que token não foi encontrado
+						// ou repositório de chaves públicas não foi configurado.
+						// Tentar carregar token/keystore novamente? / Logon sem certificado digital?
+
+						tryAgain = false;
+                        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                            System.out.println("não achou token");
+                        }
+						
+						dc.destroy();
+						System.gc();
+
+						String redirect = this.getCodeBase().toExternalForm() + "login.php";
+						try {
+							this.getAppletContext().showDocument(new URL(redirect));
+						} catch (MalformedURLException e) {
+							// TODO Bloco catch gerado automaticamente
+                            if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                                e.printStackTrace();
+                            }
+						}
+
+						//int resultado = JOptionPane.showConfirmDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+						//	"Deseja tentar novamente?",
+						//	"Token não foi encontrado!",
+						//	JOptionPane.OK_CANCEL_OPTION,
+						//	JOptionPane.QUESTION_MESSAGE);
+
+						//if (resultado == JOptionPane.OK_OPTION){
+						//	tryAgain = true;
+						//}
+						//else {
+
+						//}
+
+						// if () {
+						//	tryAgain = true;
+						//}
+				}
+
+			}
+			catch (SSLHandshakeException e){
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+				dc.destroy();
+				System.gc();
+				//JOptionPane.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), "Não foi possível validar o certificado do servidor!");
+				//DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+				//		"Não foi possível validar o certificado do servidor!", this.setup);
+                DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+						this.setup.getLang("ExpressoCertLoginMessages", "LoginApplet001"), this.setup);
+
+				Thread.yield();
+
+				tryAgain = true;
+			}
+			catch (HttpException e) {
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+				
+				tryAgain = true;
+				Thread.yield();
+			}
+			catch (GeneralSecurityException e){
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+				
+				//DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+				//		"Algum erro ocorreu ao realizar o logon!", this.setup);
+                DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+						this.setup.getLang("ExpressoCertLoginMessages", "LoginApplet002"), this.setup);
+
+				Thread.yield();
+				tryAgain = true;
+			}
+			catch (IOException e) {
+				dc.destroy();
+				System.gc();
+
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+                //System.out.println("Classe que originou o erro de IO: " + e.getClass().getCanonicalName());;
+
+                Throwable cause = null;
+                if ((cause = e.getCause()) != null){
+                    if (cause instanceof javax.security.auth.login.LoginException){
+                        //DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), "PIN incorreto!", this.setup);
+                        DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), this.setup.getLang("ExpressoCertLoginMessages", "LoginApplet003"), this.setup);
+                    } else {
+                        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                            System.out.println("Exception lançada: " + cause.getClass().getCanonicalName());
+                        }
+                    }
+                }
+                else {
+                    if (e instanceof java.net.ConnectException){
+                        //DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+                        //        "Não foi possível fazer a conexão com o servidor do Expresso.\n" +
+                        //        "Verifique suas configurações de rede/proxy.", this.setup);
+                        DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+                                this.setup.getLang("ExpressoCertLoginMessages", "LoginApplet004"), this.setup);
+                    } else {
+                        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                            System.out.println("Exception lançada: " + e.getClass().getCanonicalName());
+                        }
+                    }
+                }
+
+				Thread.yield();
+                tryAgain = true;
+			}
+			catch (ProviderException e) {
+				// Tentar novamente / cancel
+				//JOptionPane.showMessageDialog(this, "Token foi removido! Favor inserir seu token");
+
+				dc.destroy();
+				System.gc();
+
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+
+/*
+				JOptionPane optionPane = new JOptionPane("Token foi removido\nDeseja tentar carregá-lo novamente?",
+						JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
+				JDialog dialog = new JDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), true);
+				dialog.setContentPane(optionPane);
+				//Posicionando no centro da tela.
+
+				Dimension mySize = dialog.getSize();
+				Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+				dialog.setLocation(screenSize.width/2 - (mySize.width/2), screenSize.height/2 - (mySize.height/2));
+				dialog.setVisible(true);
+
+				Object selectedValue = optionPane.getValue();
+
+				if (selectedValue == null){
+
+				}
+				else {
+					if (selectedValue instanceof Integer){
+						Integer
+					}
+				}
+*/
+
+				int resultado = DialogBuilder.showConfirmDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+						//"Deseja tentar carregá-lo novamente?",
+                        this.setup.getLang("ExpressoCertLoginMessages", "LoginApplet005"),
+						JOptionPane.QUESTION_MESSAGE,
+						JOptionPane.OK_CANCEL_OPTION, this.setup);
+
+/*				int resultado = JOptionPane.showConfirmDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this),
+						"Deseja tentar carregá-lo novamente?",
+						"Token foi removido!",
+						JOptionPane.OK_CANCEL_OPTION,
+						JOptionPane.QUESTION_MESSAGE);
+*/
+
+				if (resultado == JOptionPane.OK_OPTION){
+					tryAgain = true;
+				}
+				else {
+					tryAgain = false;
+				}
+
+				Thread.yield();
+			}
+
+		}
+		while (tryAgain);
+
+		// chama o método que cria a interface na thread de lançamento de eventos (Event Dispatching Thread).
+		//try
+		//{
+		//	SwingUtilities.invokeAndWait(new Runnable(){
+
+				/* (non-Javadoc)
+				 * @see java.lang.Runnable#run()
+				 */
+		//		public void run() {
+		//			createGUI();
+		//		}
+
+		//	});
+		//} catch (Exception e){
+		//	System.out.println("Método createGUI não pôde ser carregado: " + e.getMessage());
+		//}
+
+	}
+
+	/*
+
+	public void doLogin(String[] credentials){
+
+		if (Integer.valueOf(credentials[0]) == 0){
+
+			// Pega usuário e senha de credentials[1] e credentials[2], respectivamente
+			// adiciona na página e faz o submit.
+			JSObject document = (JSObject) JSObject.getWindow( this ).getMember("document");
+			JSObject loginForm =  (JSObject) document.getMember("flogin");
+			JSObject loginField = (JSObject) loginForm.getMember("user");
+			loginField.setMember("value", credentials[1]);
+
+			JSObject passwdField = (JSObject) loginForm.getMember("passwd");
+			passwdField.setMember("value", credentials[2]);
+
+			loginForm.call("submit", null);
+
+		}
+		else if (Integer.valueOf(credentials[0]) == 6){
+
+			//JOptionPane.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), credentials[1]);
+			DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), credentials[1]);
+			String redirect = this.getCodeBase().toExternalForm() + "login.php?cd=98&ts=202";
+			try {
+				this.getAppletContext().showDocument(new URL(redirect));
+			} catch (MalformedURLException e) {
+				// TODO Bloco catch gerado automaticamente
+				e.printStackTrace();
+			}
+		}
+		else {
+			// Mostra mensagem de erro para o usuário
+			//JOptionPane.showMessageDialog(this, credentials[1]);
+			DialogBuilder.showMessageDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), credentials[1]);
+		}
+
+	}
+
+	*/
+
+	/**
+	 * Destrói a Applet, executando códigos para desregistrar tokens, keystores, etc.
+	 */
+	@Override
+	public void stop() {
+		//super.destroy();
+        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+            System.out.println("Finalizando Applet de Login!");
+        }
+		
+		this.dc.destroy();
+		this.dc = null;
+		//Component[] components = this.getComponents();
+		//for (Component component : components){
+		//	this.remove(component);
+		//	component = null;
+		//}
+		System.gc();
+	}
+
+	/**
+	 * Cria a interface e seleciona o Painel que será exibido de acordo com o parâmetro useCertificate
+	 */
+	/*
+	private void createGUI(){
+
+		this.setLayout(new FlowLayout());
+		this.getContentPane().setBackground(Color.WHITE);
+
+		JSObject document = (JSObject) JSObject.getWindow( this ).getMember("document");
+		JSObject loginForm =  (JSObject) document.getMember("flogin");
+		JSObject loginField = (JSObject) loginForm.getMember("user");
+
+		uid = loginField.getMember("value").toString();
+
+		pinPanel = new PinCodePanel(uid, true); // mostra pinCodeField
+		alreadyLoadedPanel = new PinCodePanel(uid, false); // não mostra pinCodeField
+		loginPanel = new LoginPanel(uid);
+
+		switch (useCertificate){
+			case DigitalCertificate.KEYSTORE_NOT_DETECTED:
+				// Mostra interface de login com usuário e senha
+				this.getContentPane().add(loginPanel);
+				break;
+			case DigitalCertificate.KEYSTORE_ALREADY_LOADED:
+				// Mostra interface de login sem pedir o pin
+				this.getContentPane().add(alreadyLoadedPanel);
+				break;
+			default:
+				// Mostra interface de login pedindo o pin
+				this.getContentPane().add(pinPanel);
+
+		}
+
+		this.setVisible(true);
+
+	}
+	*/
+
+	/**
+	 * Ação executada quando o Botão cancel de PinPanel for pressionado
+	 *
+	 */
+	/*
+	private void cancelButtonActionPerformed(){
+
+		pinPanel.setVisible(false);
+		this.remove(pinPanel);
+		this.add(loginPanel);
+		loginPanel.setVisible(true);
+		this.setVisible(true);
+		pinPanel.clearPasswordField();
+
+	}
+	*/
+
+	/**
+	 * Ação realizada quando o usuário pressiona o botão Ok ou Prossegir
+	 * nas telas de login com pin ou sem o pin, respectivamente
+	 *
+	 */
+	/*
+	private void okButtonActionPerformed(){
+		// atribui o valor da senha para o atributo Pin
+		char[] pin = pinPanel.getPin();
+		String[] returnCode = null;
+
+		// abre a keystore com o pin e recupera as credenciais do usuário
+		try {
+			dc.openKeyStore(pin);
+
+			returnCode = dc.getCredentials(new String(pin));
+
+			assert (returnCode != null): "Valor de returnCode não pode ser nulo";
+
+			if (Integer.valueOf(returnCode[0]) == 0){
+
+				// Pega usuário e senha de returnCode[1] e returnCode[2], respectivamente
+				// adiciona na página e faz o submit.
+				JSObject document = (JSObject) JSObject.getWindow( this ).getMember("document");
+				JSObject loginForm =  (JSObject) document.getMember("flogin");
+				JSObject loginField = (JSObject) loginForm.getMember("user");
+				loginField.setMember("value", returnCode[1]);
+
+				JSObject passwdField = (JSObject) loginForm.getMember("passwd");
+				passwdField.setMember("value", returnCode[2]);
+
+				this.pinPanel.clearPasswordField();
+				loginForm.call("submit", null);
+
+			}
+			else {
+				// Mostra mensagem de erro para o usuário
+				showGetCredentialsError(returnCode[1]);
+			}
+
+		} catch (SSLHandshakeException e){
+			e.printStackTrace();
+			JOptionPane.showMessageDialog(this, "Não foi possível validar o certificado do servidor!");
+		} catch (HttpException e) {
+			e.printStackTrace();
+		} catch (GeneralSecurityException e){
+			e.printStackTrace();
+		} catch (IOException e) {
+			JOptionPane.showMessageDialog(this, "Senha não confere!");
+		} catch (ProviderException e) {
+			JOptionPane.showMessageDialog(this, "Token foi removido!");
+		} finally {
+			this.pinPanel.clearPasswordField();
+		}
+
+	}
+	*/
+
+	/**
+	 * Ação realizada quando o usuário pressiona o botão acessar na
+	 * tela de login com usuário e senha
+	 *
+	 */
+	/*
+	private void acessarButtonActionPerformed(){
+
+		// Pega usuário e senha dos campos tfUsername e pfPasswd, respectivamente,
+		// adiciona na página e faz o submit.
+		String username = this.loginPanel.tfUsername.getText();
+		String passwd = String.valueOf(this.loginPanel.pfPasswd.getPassword());
+
+		JSObject document = (JSObject) JSObject.getWindow( this ).getMember("document");
+		JSObject loginForm =  (JSObject) document.getMember("flogin");
+		JSObject loginField = (JSObject) loginForm.getMember("user");
+		loginField.setMember("value", username);
+
+		JSObject passwdField = (JSObject) loginForm.getMember("passwd");
+		passwdField.setMember("value", passwd);
+
+		this.loginPanel.clearPasswordField();
+		loginForm.call("submit", null);
+
+	}
+	*/
+
+	/**
+	 * Mostra mensagem "Campo PIN não foi preenchido!" quando o botão Ok for pressionado
+	 *  e o campo pin estiver vazio
+	 *
+	 */
+	/*
+	private void okButtonPinEmpty(){
+		JOptionPane.showMessageDialog(this, "Campo PIN não foi preenchido!");
+	}
+	*/
+
+	/**
+	 * Mostra mensagem de erro recebida de dc.getCredentials();
+	 * TODO: Mudar este método para ser mais genérico... Todas as mensagens de erro devem ser passadas para este
+	 * 		método para serem exibidas.
+	 * @param errorMsg Mensagem de erro que deve ser exibida
+	 */
+	/*
+	private void showGetCredentialsError(String errorMsg){
+		JOptionPane.showMessageDialog(this, errorMsg);
+	}
+
+	private final class PinCodePanel extends JPanel  implements ActionListener{
+*/
+		/**
+		 * Gerado automaticamente
+		 */
+	/*
+		private static final long serialVersionUID = 1003857725229120014L;
+
+		JPanel pNorte, pCentro, pSul;
+		JButton btOk, btCancel;
+		JLabel lTitle, lPin;
+		JPasswordField pfPin;
+
+		// TODO: Implementar um construtor default (sem parâmetros);
+		public PinCodePanel(String uid, boolean showPinField){
+
+			this.setLayout(new BorderLayout());
+			Dimension panelsDimension = new Dimension(300, 42);
+			Dimension fieldsDimension = new Dimension(80, 20);
+
+			pNorte = new JPanel(new FlowLayout(FlowLayout.CENTER));
+			pNorte.setBackground(Color.WHITE);
+			pNorte.setPreferredSize(panelsDimension);
+
+			pCentro = new JPanel(new FlowLayout(FlowLayout.CENTER));
+			pCentro.setBackground(Color.WHITE);
+			pCentro.setPreferredSize(panelsDimension);
+
+			pSul = new JPanel(new FlowLayout(FlowLayout.CENTER));
+			pSul.setBackground(Color.WHITE);
+			pSul.setPreferredSize(panelsDimension);
+
+			JPanel pTitle = new JPanel(new GridLayout(2, 0));
+			pTitle.setBackground(Color.WHITE);
+			JLabel lTitle2 = null;
+
+
+			if (showPinField){
+				lTitle = new JLabel("Entre com o seu PIN:");
+				lTitle.setHorizontalTextPosition(SwingConstants.CENTER);
+
+				pfPin = new JPasswordField(20);
+				pfPin.setPreferredSize(fieldsDimension);
+				pfPin.addActionListener(this);
+				pfPin.setActionCommand("ok");
+				pCentro.add(pfPin);
+				btOk = new JButton("Ok");
+				btCancel = new JButton("Cancel");
+			}
+			else {
+				lTitle = new JLabel("Token inserido e desbloqueado");
+				lTitle.setHorizontalTextPosition(SwingConstants.CENTER);
+				lTitle2 = new JLabel(" pelo usuário " + uid + " foi detectado:");
+				lTitle2.setHorizontalTextPosition(SwingConstants.CENTER);
+				btOk = new JButton("Prossegir");
+			}
+
+			JPanel pTitle1 = new JPanel(new FlowLayout(FlowLayout.CENTER));
+			pTitle1.setPreferredSize(new Dimension(300, 20));
+			pTitle1.setBackground(Color.WHITE);
+			pTitle1.add(lTitle);
+			pTitle.add(pTitle1);
+			if (lTitle2 != null) {
+				JPanel pTitle2 = new JPanel(new FlowLayout(FlowLayout.CENTER));
+				pTitle2.setPreferredSize(new Dimension(300,20));
+				pTitle2.setBackground(Color.WHITE);
+				pTitle.add(pTitle2);
+				pTitle2.add(lTitle2);
+			}
+
+			pNorte.add(pTitle);
+
+			btOk.setMnemonic(KeyEvent.VK_ENTER);
+			btOk.setActionCommand("ok");
+			btOk.addActionListener(this);
+			pSul.add(btOk);
+
+			if (btCancel != null){
+				btCancel.setActionCommand("cancel");
+				btCancel.addActionListener(this);
+				pSul.add(btCancel);
+			}
+
+			this.add(pNorte, BorderLayout.NORTH);
+			this.add(pCentro, BorderLayout.CENTER);
+			this.add(pSul, BorderLayout.SOUTH);
+		}
+
+		// design ruim!!! azar :-S
+		public char[] getPin() {
+			if (pfPin == null){
+				return null;
+			}
+			return pfPin.getPassword();
+		}
+
+		void clearPasswordField(){
+			if (pfPin != null){
+				pfPin.setText("");
+			}
+		}
+
+		public void actionPerformed(ActionEvent evt) {
+
+			String command = evt.getActionCommand();
+
+			// Atribui o valor digitado para o campo
+			// senha e esconde a interface e assina o certificado
+			if (command.equals("ok")){
+
+				if (pfPin != null && pfPin.getPassword().length <= 0){
+					// Mostra um aviso de que o campo PIN não foi preenchido
+					okButtonPinEmpty();
+				}
+				else {
+					okButtonActionPerformed();
+				}
+
+			}
+			else if (command.equals("cancel")){
+				cancelButtonActionPerformed();
+			}
+		}
+
+
+	}
+
+	private final class LoginPanel extends JPanel implements ActionListener{
+*/
+		/**
+		 *
+		 */
+	/*
+		private static final long serialVersionUID = -8604718192554937222L;
+		JButton btAcessar;
+		JTextField tfUsername;
+		JPasswordField pfPasswd;
+
+		public LoginPanel(String uid){
+
+			this.setLayout(new BorderLayout());
+
+			JPanel panelCredentials = new JPanel(new GridLayout(2, 0));
+			panelCredentials.setBackground(Color.WHITE);
+			panelCredentials.setPreferredSize(new Dimension(280, 84));
+
+			JPanel panelUsername = new JPanel(new FlowLayout(FlowLayout.CENTER));
+			panelUsername.setBackground(Color.WHITE);
+			panelUsername.setPreferredSize(new Dimension(300, 42));
+
+			JLabel labelUsername = new JLabel("Usuário");
+			panelUsername.add(labelUsername);
+			labelUsername.setPreferredSize(new Dimension(100, 40));
+
+			tfUsername = new JTextField(15);
+			tfUsername.setText(uid);
+			panelUsername.add(tfUsername);
+
+			JPanel panelPasswd = new JPanel(new FlowLayout(FlowLayout.CENTER));
+			panelPasswd.setPreferredSize(new Dimension(300, 42));
+			JLabel labelPasswd = new JLabel("Senha");
+			panelPasswd.add(labelPasswd);
+			panelPasswd.setBackground(Color.WHITE);
+			labelPasswd.setPreferredSize(new Dimension(100, 20));
+
+			pfPasswd = new JPasswordField(15);
+			panelPasswd.add(pfPasswd);
+
+			panelCredentials.add(panelUsername);
+			panelCredentials.add(panelPasswd);
+			this.add(panelCredentials, BorderLayout.CENTER);
+
+			btAcessar = new JButton("Acessar");
+			btAcessar.setActionCommand("acessar");
+			btAcessar.addActionListener(this);
+			JPanel panelBtAcessar = new JPanel(new FlowLayout(FlowLayout.CENTER));
+			panelBtAcessar.setBackground(Color.WHITE);
+			panelBtAcessar.setPreferredSize(new Dimension(300, 42));
+			panelBtAcessar.add(btAcessar);
+
+			this.add(panelBtAcessar, BorderLayout.SOUTH);
+			this.setVisible(true);
+
+		}
+
+		public void actionPerformed(ActionEvent e) {
+			if (e.getActionCommand().equals("acessar")){
+				acessarButtonActionPerformed();
+			}
+
+		}
+
+		void clearPasswordField(){
+			pfPasswd.setText("");
+		}
+
+	}
+	*/
+
+}
Index: /branches/2.0/security/ExpressoCertLogin/TestApplet.html
===================================================================
--- /branches/2.0/security/ExpressoCertLogin/TestApplet.html	(revision 1182)
+++ /branches/2.0/security/ExpressoCertLogin/TestApplet.html	(revision 1182)
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>Insert title here</title>
+
+<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93">
+  <param name="type" value="application/x-java-applet;version=1.4">
+  <param name="code" value="LoginApplet.class">
+  <param name="archive" value="ExpressoCert.jar">
+    <comment>
+      <embed code="LoginApplet.class" archive="ExpressoCert.jar"
+        type="application/x-java-applet;version=1.4">
+        <noembed>
+          No Java Support.
+        </noembed>
+      </embed>
+    </comment>
+  </object>
+</head>
+<body>
+
+</body>
+</html>
Index: /branches/2.0/security/ExpressoCertLogin/build.xml
===================================================================
--- /branches/2.0/security/ExpressoCertLogin/build.xml	(revision 1182)
+++ /branches/2.0/security/ExpressoCertLogin/build.xml	(revision 1182)
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="ExpressoCertLogin_trunk" default="default" basedir=".">
+    <description>Builds, tests, and runs the project ExpressoCertLogin_trunk.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be 
+    used for execution of your tasks. These targets are usually executed 
+    before and after some main targets. They are: 
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported 
+    nbproject/build-impl.xml file. 
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are: 
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
+      run:                      execution of project 
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="ExpressoCertLogin_trunk-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on 
+    the compile target as the regular run target does. Again, for a list of available 
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file. 
+
+    -->
+</project>
Index: /branches/2.0/security/cas/todos.cer
===================================================================
--- /branches/2.0/security/cas/todos.cer	(revision 1182)
+++ /branches/2.0/security/cas/todos.cer	(revision 1182)
@@ -0,0 +1,672 @@
+-----BEGIN CERTIFICATE-----
+MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy
+MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG
+A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt
+YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD
+VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA
+isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj
+Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50
+QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt
+bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR
+yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID
+AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0
+cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f
+BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj
+cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1
+U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl
+YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos
+SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/
+t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u
+mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb
+K+9A46sd33oqK8n8
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEeDCCA2CgAwIBAgIBEzANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wNTAzMjQyMDAxMDBaFw0xMTEwMjQy
+MzU5MDBaMIGPMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE7MDkG
+A1UECxMyU2VydmljbyBGZWRlcmFsIGRlIFByb2Nlc3NhbWVudG8gZGUgRGFkb3Mg
+LSBTRVJQUk8xLjAsBgNVBAMTJUF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBkbyBT
+RVJQUk8gdjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0WxXnyzz5
+J03+aBrPJ5AIoCYhdp8W3fMneH7l4jwMDiF9C8/bsgDYUc6hGC3l1PVwuYf/VGwB
+p6kYPaJX/W88Tm/4Vq66yYUIsa7q8yq90CD387rbaCu8qbGEIrOV18Fp63ohLPI0
+24FoNBI4WwlRb6aktVOkSBr0UeIAc2x4vjJWpSzvCg2eyRQyOz9nKVLwQJLkYk1f
+hac/aoj18ksK8zKQieHtls2fW51p4DI4jaM42kHHxV9eJCml4wbrNiTy+1NSi/VX
+JAXWCGcNoHM5hmkCDbfQB2aWrZDLjJ8RS0E32ilT3A30DqdkOIhKgbmjfQuBTZWJ
+8ZA+msEYaOl5AgMA05ejgbcwgbQwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2Fj
+cmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFjcmFpei5jcmwwEgYDVR0gBAswCTAH
+BgVgTAEBAjAdBgNVHQ4EFgQU4osVQdt1OSm8HFR7+1E/FAkS8rQwHwYDVR0jBBgw
+FoAUivrxV4QREzWQQvpXSVRpDaTE8DcwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
+/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGJSHMCH3re9mCmtnVH+btonLi8M
+EJgtYDwwOkLvknnwI+0WimHsFDM7ABcaKw4N5OZwAlcSFla4i1vbFjools4HD4z+
+fMwbHIhifTNLQKW49Dm+XqnBIe/UIbNXfDdQmKk770WhsljALMC53k/phxPxHN3y
+qrD06YwDaQKLbuAO800ycCUeIvvxB698oSdrC/m2jf+Wt6NZMybqw7Tp2qBR+c/v
+CZW1g6Qbij5lOm2ADrpXpjiDKnmsUmff9uvByix2mbTHRNa9E+eeev7ZN+PpSLhZ
+5x8xKisZxBXdBL+2aeGaNa81sWculWLam3DI6bARrzAtRrI0cVev2kkSWks=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFdzCCBF+gAwIBAgISMjAwNTA0MDQxNTUwNTYwMDAxMA0GCSqGSIb3DQEBBQUA
+MIGPMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE7MDkGA1UECxMy
+U2VydmljbyBGZWRlcmFsIGRlIFByb2Nlc3NhbWVudG8gZGUgRGFkb3MgLSBTRVJQ
+Uk8xLjAsBgNVBAMTJUF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBkbyBTRVJQUk8g
+djEwHhcNMDUwNDA0MTYyNjU5WhcNMTExMDI0MjM1OTAwWjCBpjELMAkGA1UEBhMC
+QlIxEzARBgNVBAoTCklDUC1CcmFzaWwxOzA5BgNVBAsTMlNlcnZpY28gRmVkZXJh
+bCBkZSBQcm9jZXNzYW1lbnRvIGRlIERhZG9zIC0gU0VSUFJPMQ8wDQYDVQQLEwZD
+U1BCLTExNDAyBgNVBAMTK0F1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBkbyBTRVJQ
+Uk8gRmluYWwgdjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7axUl
+Kt/w/tTlxreEIOPdewTpE73tZD7G+pABsmGsND+wkhXdsE8u9muZz2QgUC0FNQnz
+BtxEfIVFIQtS6RVQ2y4oF8FkpnSdamxFXfd3UfxY5IADM3e7GzLxevLtNd2CfB34
+41inYkkbcF1x37zGDktKWimOutXBIeKhADY/xkLP1rb1ud8hfxwmlZvR7kERtq1+
+3ha54R3MH3lvGjvXUdWrJ7cyqhGhuBRPOQTMLABoTvp1/6tVkDeq+xqoh8qxB+bw
+MKlLmhdz/IGJzajO7rqkqiFi+hV6ULFGYna8aAoxYMXwGY5CaY0+vmZiVgWHVc+o
+F/y1E4T23N06EhDHAgMA5fGjggGyMIIBrjASBgNVHRMBAf8ECDAGAQH/AgEAMCIG
+A1UdIwEBAAQYMBaAFOKLFUHbdTkpvBxUe/tRPxQJEvK0MCAGA1UdDgEBAAQWBBQy
+OJbH7kRk6ZqqFV3gCLSNiUdRojAOBgNVHQ8BAf8EBAMCAQYwggEBBgNVHSABAQAE
+gfYwgfMwTwYGYEwBAgEQMEUwQwYIKwYBBQUHAgEWN2h0dHBzOi8vY2NkLnNlcnBy
+by5nb3YuYnIvYWNzZXJwcm8vZG9jcy9kcGNhY3NlcnByby5wZGYwTwYGYEwBAgMN
+MEUwQwYIKwYBBQUHAgEWN2h0dHBzOi8vY2NkLnNlcnByby5nb3YuYnIvYWNzZXJw
+cm8vZG9jcy9kcGNhY3NlcnByby5wZGYwTwYGYEwBAgERMEUwQwYIKwYBBQUHAgEW
+N2h0dHBzOi8vY2NkLnNlcnByby5nb3YuYnIvYWNzZXJwcm8vZG9jcy9kcGNhY3Nl
+cnByby5wZGYwPQYDVR0fAQEABDMwMTAvoC2gK4YpaHR0cDovL2NjZC5zZXJwcm8u
+Z292LmJyL2xjci9hY3NlcnByby5jcmwwDQYJKoZIhvcNAQEFBQADggEBACDVTheR
+VBDVPIygP/NdI/sDg8iSWRNY4do3PraFAPL1wl4n3sbdMPH4jcvf4HlCUuiKnMA5
+QGfiMhkFD8OKYnxE2KscApC/Sg2FidkoPBlq9eH2QVmkxNDZ9ZlMoZgQ1Wlq4yoH
+Rwsi24Vv8J6xnf+ebUu6RM3BCYN4YwgyQCqCxKjhAKMwcthPgDdjZFTrMzvvCN5O
+lf1tYZAFR7J7ZBK56JfpIBctoR5nJSkU+hHGUFNG7erySLvXmDzF582Wgg11aj5R
+XEN/q9YdPvUbGRFU+aQqVuWELpS7Kj6Otwi9Ai+Fi5OVWaRYofuhPpvWXjLv+RvI
+cHed5wX+WP7pfSw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEjDCCA3SgAwIBAgIBGDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wNjA5MjAxODM5MDBaFw0xMTA5MjAy
+MzU5MDBaMIGjMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG
+A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt
+YWNhbyAtIElUSTFAMD4GA1UEAxM3QXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIGRh
+IFByZXNpZGVuY2lhIGRhIFJlcHVibGljYSB2MTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBANdjRqQ+eeeyu3tHVrgNIqQ4I8TcxbJPDfr5t9t6skPHFGFj
+m6rgXIejtmtN83NFmGhLIB2FZIejEkg0kHv2akuoz1qBTTt5BNj5lyHQBVobrH3V
+BTqyUfXRLCnykca6lUugk5H2LjqeA0knsSopu9QHKwgy9i//MBK7mdU47VHQaJa8
+ONM1OC5MurfcmhVAIu3FaYTaOyRhukvoVMta1G46bXARkgN6wYIBZHqIQGUZHM3D
++BsaK59p1cnDo2/kH4LSPpEcrFkk/zbQPzsuebbRIARZLWviIubw4B8St5gTUh/m
+2H/olIbJylzq+X4OMiThz1XmBzNueMlp7ClzYA0CAwDH16OBtzCBtDA9BgNVHR8E
+NjA0MDKgMKAuhixodHRwOi8vYWNyYWl6LmljcGJyYXNpbC5nb3YuYnIvTENSYWNy
+YWl6LmNybDASBgNVHSAECzAJMAcGBWBMAQEBMB0GA1UdDgQWBBS+3nlKjQejae0L
+vkXEPu3VxOup8jAfBgNVHSMEGDAWgBSK+vFXhBETNZBC+ldJVGkNpMTwNzAOBgNV
+HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
+Nmqku7VHja7/TQkGJvY7EqMfDdKaVhFq4XVAdkedEjOIcMxg7/1lgtlgbZK0ttYu
+KAJ+ODxJGjNozfinNdebq/vQg11SMCn+AmFHWqMWjNhmLjCdfPjGPavzJmlOlWTg
+0xTo53Kd6G8mAk86rhEz+5tAUWwmBVdDpXqeowk/YJ08M/XwMZpXxIj8RjWwd/Ma
+HPysCZBmkOk8iK1kP+hZOTZUWOAs98LGN9O+PY7svclXQ+6LfZ+JXJXweLtm3Yf9
+/P4MHYLeiCjV/dUN0ygKrdUIbxR1ouxRZ0UShO1ZJQ0ZgC61S5mIoCMpBKlXVk0h
+U7g2qUogTG1hnVvP32Rjkw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEHTCCAwWgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxNDAyBgNVBAMTK0F1
+dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBSYWl6IEJyYXNpbGVpcmEgdjEwHhcNMDgx
+MDEzMTg0ODE3WhcNMTgxMDEzMTg0ODE3WjBkMQswCQYDVQQGEwJCUjETMBEGA1UE
+ChMKSUNQLUJyYXNpbDFAMD4GA1UEAxM3QXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3Jh
+IGRhIFByZXNpZGVuY2lhIGRhIFJlcHVibGljYSB2MjCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBANfQ/zMCWuG5O0ID19fjM/agsThhHiMlm/wriB/9rIIk
+UIn6pHXgMx/ADYOWQsXfJaa3d7VjIC8U48N7qWJDgHM4ZB8nZyEynMRkiFlGK7bC
+iSqlgrUx84SDbExIPeFgxnjhxuog2bAqcNz0b1sSx66wEEvtrb4afAlvZ+ht92Fk
+j4thQcvYfm4S4KawdaTP6P0OZ6KXIvUs+pBQW8MXBV9JmpEYY1aa65Qa4uQn4a7Q
+jPwJ/fk5Dka10VlSNwYu8Nfvp3kO8fM6+4+o0zRXFU72jAeGQ9zwlr3wjQ66QNdY
+3SaSi0LUkqJjc+66GXfOq6So/SHP/v/SDsiqpnOyMl8CAwC4J6OBpTCBojAOBgNV
+HQ8BAf8EBAMCAQYwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2FjcmFpei5pY3Bi
+cmFzaWwuZ292LmJyL0xDUmFjcmFpenYxLmNybDAfBgNVHSMEGDAWgBRCsixcdAEH
+vpv/VTM77im7XZG/BjAdBgNVHQ4EFgQUNb96TUft5yGIzSaFOuCfUZSDaGMwDwYD
+VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAUeQIKToPUWYHWfLmO1Er
+zlmbH6CYW9JUSlgxz8L/bbnfFuyl+G2siCPhXonCeczBd2P9/akdH40iqVdEAvWl
+DPmyLgkzwR7jxnL2kBCJ228Ap5G8M8E39DvYmYTYGakZaJCrwa5PqZOIzzOtuua0
+X+DRFzz4p+TjU22h159iCE7AIfKROXUBELLW3z1psWjRQtteZiDKzGLsth5fRhJl
+0nkLfcyMVGpneSLkhrfp3Ip38I1GNz/S4EyHzZMFut1JGRBFZwa1s1YHWFEhs0rO
+aybq6AenUjXSyCocajlAk1okyWtQjTyA+PlQtEZUm6kZeFrM1+MuCMQf2RWGwgnj
+XQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEgDCCA2igAwIBAgIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxNDAyBgNVBAMTK0F1
+dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBSYWl6IEJyYXNpbGVpcmEgdjEwHhcNMDgw
+NzI5MTkxNzEwWhcNMjEwNzI5MTkxNzEwWjCBlzELMAkGA1UEBhMCQlIxEzARBgNV
+BAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25hbCBkZSBU
+ZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxNDAyBgNVBAMTK0F1dG9yaWRh
+ZGUgQ2VydGlmaWNhZG9yYSBSYWl6IEJyYXNpbGVpcmEgdjEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDOHOi+kzTOybHkVO4J9uykCIWgP8aKxnAwp4CM
+7T4BVAeMGSM7n7vHtIsgseL3QRYtXodmurAH3W/RPzzayFkznRWwn5LIVlRYijon
+ojQem3i1t83lm+nALhKecHgH+o7yTMD45XJ8HqmpYANXJkfbg3bDzsgSu9H/766z
+Yn2aoOS8bn0BLjRg3IfgX38FcFwwFSzCdaM/UANmI2Ys53R3eNtmF9/5Hw2CaI91
+h/fpMXpTT89YYrtAojTPwHCEUJcV2iBL6ftMQq0raI6j2a0FYv4IdMTowcyFE86t
+KDBQ3d7AgcFJsF4uJjjpYwQzd7WAds0qf/I8rF2TQjn0onNFAgMBAAGjgdQwgdEw
+TgYDVR0gBEcwRTBDBgVgTAEBADA6MDgGCCsGAQUFBwIBFixodHRwOi8vYWNyYWl6
+LmljcGJyYXNpbC5nb3YuYnIvRFBDYWNyYWl6LnBkZjA/BgNVHR8EODA2MDSgMqAw
+hi5odHRwOi8vYWNyYWl6LmljcGJyYXNpbC5nb3YuYnIvTENSYWNyYWl6djEuY3Js
+MB0GA1UdDgQWBBRCsixcdAEHvpv/VTM77im7XZG/BjAPBgNVHRMBAf8EBTADAQH/
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAWWyKdukZcVeD/qf0
+eg+egdDPBxwMI+kkDVHLM+gqCcN6/w6jgIZgwXCX4MAKVd2kZUyPp0ewV7fzq8TD
+GeOY7A2wG1GRydkJ1ulqs+cMsLKSh/uOTRXsEhQZeAxi6hQ5GArFVdtThdx7KPoV
+caPKdCWCD2cnNNeuUhMC+8XvmoAlpVKeOQ7tOvR4B1/VKHoKSvXQw2f3jFgXbwoA
+oyYQtGAiOkpIpdrgqYTeQ9ufQ6c/KARHki/352R1IdJPgc6qPmQO4w6tVZp+lJs0
+wdCuaU4eo9mzh1facMJafYfN+b833u1WNfe3Ig5Pkrg/CN+cnphe8m+5+pss+M1F
+2HKyIA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEPzCCAyegAwIBAgIBFDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wNTA2MzAxNDA5MDBaFw0xMTEwMzAy
+MzU5MDBaMFcxCzAJBgNVBAYTAmJyMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSAwHgYD
+VQQLExdDYWl4YSBFY29ub21pY2EgRmVkZXJhbDERMA8GA1UEAxMIQUMgQ0FJWEEw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXx2dOOF3Pt4yaK5F6yhmM
+72DRUrBdPGOALpIpzLcKk7XZ8myuFMVxHEkQV6DTpGwooUjhsFTqGH9Cb4rixj9j
+mh4Rek9K+rHvxwTHBN94GJ1AP65s/ytucTSvldACimqNPAg/htj5MOTwKiTl0Hnf
+5yjE1GxAcaOquwz6siyYUZ/msDGCVJGdAepfXwjdZo3AoqTZ/VHWahvHKyVUvxF6
+q6gCLFpor259LB9vGBiOIcaSdvL0P5MPtkV1UCH+QxpEZTv4Uo+c4nVZTysheLSt
+6aoKZdDUZaELGnyYN5bpWWA/lWFrBIpS9I5Y87broR671rpDiLlckShIPMmWDuRR
+AgMBAAGjgbcwgbQwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3Bi
+cmFzaWwuZ292LmJyL0xDUmFjcmFpei5jcmwwEgYDVR0gBAswCTAHBgVgTAEBCTAd
+BgNVHQ4EFgQUHPxV/WT83BbMZSzn+XQH3rpLK9YwHwYDVR0jBBgwFoAUivrxV4QR
+EzWQQvpXSVRpDaTE8DcwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+DQYJKoZIhvcNAQEFBQADggEBAIPN+JFPD3nfGczyLBzGwdD6XmDfPtEpEfbQKGib
+ZekS/ip6eWoXAwWqgYRunSN5H9lukMWI6IEKcV92aSVFSQRrOPIIpZg0MiA+XCBq
+XLXqVYUmVDCtKbzDq9xrRYP21lDQk94vgKih7OUznHQspUKgNvAHJeE60oAVgeA8
+cBc+AVVmf5VWbiKOKwx3JiCoGI7g3p41fXxk4urxu/rD8lYh3FcH3v261H6aEAba
+e5eeGwcioeDH9vusedmFYpmcSDGmr8edCoOohZToFffKQJNdNSrpFt1yECQ/Nj/y
+6B+CCY1XWte4TJ2/tbIFasBxzjRDUD7luJ/Ij0X24+3EsyE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFCjCCA/KgAwIBAgIEQd8NtTANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJi
+cjETMBEGA1UEChMKSUNQLUJyYXNpbDEgMB4GA1UECxMXQ2FpeGEgRWNvbm9taWNh
+IEZlZGVyYWwxETAPBgNVBAMTCEFDIENBSVhBMB4XDTA1MDYzMDE3NDQzMloXDTEx
+MTAzMDIzNTkwMFowWjELMAkGA1UEBhMCYnIxEzARBgNVBAoTCklDUC1CcmFzaWwx
+IDAeBgNVBAsTF0NhaXhhIEVjb25vbWljYSBGZWRlcmFsMRQwEgYDVQQDEwtBQyBD
+QUlYQSBJTjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKJ9UgjioJ/+
+K2NLorz1V1rxRvqKAT4ECVuloApMuOAtDnEjfnf7aIe4wn+z8MN1Vw0TlaNeXmKI
+sPk3kndlGSu99AucYkl26JyBjoI7KWvJeenBAYvSexkIKAgXB+DoBXrcaRhULT+q
+pKJhgzQAlppsD60XqifnToxgjd4cMfgmjp+DPL2sFU1nr+ZqqbTym8AVQQ6u+48D
+BPtlrVeNWXrbcjNWL54HY9NMUB33CXNo4+qphCFCrAxwiXNRlSequZ1wpYOCkB8y
+vZjX5sXTn05+RlebZyU3PBc9Zri3t2lcZaFsIBNgCH4crpYGu6hnf0QtU+epsciX
+VcIkUto/vscCAwEAAaOCAdkwggHVMA4GA1UdDwEB/wQEAwIBBjCBowYDVR0gBIGb
+MIGYMEoGBmBMAQIBBzBAMD4GCCsGAQUFBwIBFjJodHRwOi8vaWNwLmNhaXhhLmdv
+di5ici9yZXBvc2l0b3Jpby9kcGNhY2NhaXhhLnBkZjBKBgZgTAECAwcwQDA+Bggr
+BgEFBQcCARYyaHR0cDovL2ljcC5jYWl4YS5nb3YuYnIvcmVwb3NpdG9yaW8vZHBj
+YWNjYWl4YS5wZGYwgbMGA1UdHwSBqzCBqDBuoGygaqRoMGYxCzAJBgNVBAYTAmJy
+MRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSAwHgYDVQQLExdDYWl4YSBFY29ub21pY2Eg
+RmVkZXJhbDERMA8GA1UEAxMIQUMgQ0FJWEExDTALBgNVBAMTBENSTDEwNqA0oDKG
+MGh0dHA6Ly9pY3AuY2FpeGEuZ292LmJyL3JlcG9zaXRvcmlvL0FDQ0FJWEExLmNy
+bDAfBgNVHSMEGDAWgBQc/FX9ZPzcFsxlLOf5dAfeuksr1jAdBgNVHQ4EFgQUlIqn
+HLQRdQ4B4Vnh0fnxcI6WHEowDAYDVR0TBAUwAwEB/zAZBgkqhkiG9n0HQQAEDDAK
+GwRWNy4wAwIAgTANBgkqhkiG9w0BAQUFAAOCAQEABRY0YVB0QcEOz8cYorjINJW+
+z8posP1QjmIPxNSLM96qQJ8G53uLv7q1CF2cWVvuZZnkxQqWnpSKCJr0f5X5TiLW
+7Qzq9P7xrTKJfpK+lvdDkQFZxkvhH0SaTiqg8cCwCU9xcHtMaTuYZTHF9x38fNBL
+gngc1eqF2lqjrrsvZ0kX3Y/DeT6wxQu8QteWb0tS5LnRaKRlwqgq3eWGz5ZdRr1e
+8Y3p6/PrXJXINR5c0uMZA4v3Ke8IHx30ZZK79udDIWE2/lGuWsrIRVfwUquPAQEG
+u8o+KoxeYDHeQuEuTSlTuzR5uM+q5NiNr6Pd0lFkdI9ObHeMS4gYdovE7piOOA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFlTCCBH2gAwIBAgISMjAwNjA0MDcxNTM2NDYwMDAxMA0GCSqGSIb3DQEBBQUA
+MFAxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSwwKgYDVQQDEyNB
+dXRvcmlkYWRlIENlcnRpZmljYWRvcmEgZGEgSnVzdGljYTAeFw0wNjA0MDcxNTUz
+MzZaFw0xMTA1MzExNTUzMzZaMHAxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1At
+QnJhc2lsMTUwMwYDVQQLEyxBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgZGEgSnVz
+dGljYSAtIEFDLUpVUzEVMBMGA1UEAxMMQUMgQ0FJWEEtSlVTMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPeks/OhVO9rsPjWGLmx0wb0B5MfDsOsoEOs
+nO/Yh6jAiJ0Br+fUCAIWq0ehN4AWMuiemNvGdJRqJW2I42ME5WPfgPgYNMLQAopo
+oipRo9S8C0BJm1ci0ATXbDvp68lsBHFVlo1ZmyQttnz7Vb1Mlruq6S5u6seezp2z
+STFjVwzp9XVaYnutz1uD1IeCEPIdkgBRTlXP95JjacX6xnG7g2hZ2DvY1E8wqNZq
+yyQB/qyQf1gQj7z93g51d5C1FUCSVQRarwI3zEQhfi5UrhACi4wZdBsNzk7GlTLc
+gKLKvxNLyPG804qj8hEeOUuc5FTU1visVIeNnknAGTxIHJ/DGQIDAQABo4ICRzCC
+AkMwEgYDVR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBS+OdrawXcl4K69Gs6k
+W5/AemD8ZTAdBgNVHQ4EFgQUDNDX/gMFm70hiQQ/Esavo6rXni4wDgYDVR0PAQH/
+BAQDAgEGMIIBnwYDVR0gBIIBljCCAZIwQQYGYEwBAgEWMDcwNQYIKwYBBQUHAgEW
+KWh0dHA6Ly93d3cuYWNqdXMuZ292LmJyL2FjanVzL2RwY2FqdXMucGRmMEEGBmBM
+AQICBTA3MDUGCCsGAQUFBwIBFilodHRwOi8vd3d3LmFjanVzLmdvdi5ici9hY2p1
+cy9kcGNhanVzLnBkZjBBBgZgTAECAxMwNzA1BggrBgEFBQcCARYpaHR0cDovL3d3
+dy5hY2p1cy5nb3YuYnIvYWNqdXMvZHBjYWp1cy5wZGYwQQYGYEwBAmUFMDcwNQYI
+KwYBBQUHAgEWKWh0dHA6Ly93d3cuYWNqdXMuZ292LmJyL2FjanVzL2RwY2FqdXMu
+cGRmMEEGBmBMAQJmBDA3MDUGCCsGAQUFBwIBFilodHRwOi8vd3d3LmFjanVzLmdv
+di5ici9hY2p1cy9kcGNhanVzLnBkZjBBBgZgTAECZwYwNzA1BggrBgEFBQcCARYp
+aHR0cDovL3d3dy5hY2p1cy5nb3YuYnIvYWNqdXMvZHBjYWp1cy5wZGYwOgYDVR0f
+BDMwMTAvoC2gK4YpaHR0cDovL3d3dy5hY2p1cy5nb3YuYnIvYWNqdXMvYWNqdXN2
+MS5jcmwwDQYJKoZIhvcNAQEFBQADggEBAHJhUoT96zSCDM68Gc7u40gotmTFoxN3
+JFIBOhi7aO2qABGdyRTJUq4+LzwGXk0BljF4G+sMpBZRAuy2a3sfPYFQggnK2iMl
+acRaLMkxjxhYEAMvUTptatQvsyqJAZHh/a9VKwpv98b6y1N1ZzRxQaof55w/aC5Y
+rCzal8gUSl6qJOeDF9nbarbN7FT2B5NpE34hOfLctuJG8WKr4nF4eRQR8baxpiBe
+YaAQAK0TZ14JCLipeAnivAoR+7OsIT9gk6JF+C2fQDkAWd/GX+PPsnSGJvUntoz/
+CKCkL+YS/e1kh3EqUMEXYmTKZm9lwDpzZSPVdpRieCqQNtcjXm5R2L8=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFCjCCA/KgAwIBAgIEQd8NszANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJi
+cjETMBEGA1UEChMKSUNQLUJyYXNpbDEgMB4GA1UECxMXQ2FpeGEgRWNvbm9taWNh
+IEZlZGVyYWwxETAPBgNVBAMTCEFDIENBSVhBMB4XDTA1MDYzMDE3MzgxNFoXDTEx
+MTAzMDIzNTkwMFowWjELMAkGA1UEBhMCYnIxEzARBgNVBAoTCklDUC1CcmFzaWwx
+IDAeBgNVBAsTF0NhaXhhIEVjb25vbWljYSBGZWRlcmFsMRQwEgYDVQQDEwtBQyBD
+QUlYQSBQRjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALrVcHHI/6rA
+XeTBQOwciz8TQmGqHsnJ1N3wpETdi47g7qBcSOxDv7E+Ndr/EE6nOIhNA/sGWplo
+1bgzWalYdl7fW+pzCEHaYJlSQ2crA1DgIacbyvuGSEUfHyiYBBfFv5Y8fdU1IXY7
+8rmF2VEVtU7BbT9S+7+LeLjhonqyjQk1FinsdC+YGCDjNQqbiKlkMOVUGMFRuhlu
+usD6GpZXW0Ez61cbRq81IIUBTEVvV7bAcPr2/cVCNOVlxWQv6Q3fCinvtTi4v41P
+YPad+vZec3BxyWZZzfmbT/2CbyQJMWNohNPeQ3pSEyBmLLTEcXBAXmuy0J3MfmMk
+QJbflpMKsasCAwEAAaOCAdkwggHVMA4GA1UdDwEB/wQEAwIBBjCBowYDVR0gBIGb
+MIGYMEoGBmBMAQIBBzBAMD4GCCsGAQUFBwIBFjJodHRwOi8vaWNwLmNhaXhhLmdv
+di5ici9yZXBvc2l0b3Jpby9kcGNhY2NhaXhhLnBkZjBKBgZgTAECAwcwQDA+Bggr
+BgEFBQcCARYyaHR0cDovL2ljcC5jYWl4YS5nb3YuYnIvcmVwb3NpdG9yaW8vZHBj
+YWNjYWl4YS5wZGYwgbMGA1UdHwSBqzCBqDBuoGygaqRoMGYxCzAJBgNVBAYTAmJy
+MRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSAwHgYDVQQLExdDYWl4YSBFY29ub21pY2Eg
+RmVkZXJhbDERMA8GA1UEAxMIQUMgQ0FJWEExDTALBgNVBAMTBENSTDEwNqA0oDKG
+MGh0dHA6Ly9pY3AuY2FpeGEuZ292LmJyL3JlcG9zaXRvcmlvL0FDQ0FJWEExLmNy
+bDAfBgNVHSMEGDAWgBQc/FX9ZPzcFsxlLOf5dAfeuksr1jAdBgNVHQ4EFgQUUMLw
+p1F6NCjO5zxYaNjRvc9mXhkwDAYDVR0TBAUwAwEB/zAZBgkqhkiG9n0HQQAEDDAK
+GwRWNy4wAwIAgTANBgkqhkiG9w0BAQUFAAOCAQEAGkRhJIjrHitD3BSycDYSDp2h
+SMyrzk6DhbjG/T8wLHDVgTvezM4ljdApzKsKqJb8UUPmc4gWurVBna3C9AJUBqY7
+9hvMyU/DDzNbElia7N3+nydBGpkaDLhSjbBe+bxutmUfJxncY2z4ATR+xU1hhH2j
+E8OVCM5xYxP1Y6J0W+Cq3/mLQ8cUEkgmAynfUyENtHOrhtwVGyjWxYwLBDnUIzHO
+CthdqMmAUpZg/+033Tc9ReYC5ApQ3BEU59Kg9B2+kVqOhiXFjyeUbkmFVPaovOTr
+kSP/6nyDkNYVAppyuph3a11V0unIO1aUZd/uvwI/xIhE3j/lMjiCgYyOD6Bssw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFCjCCA/KgAwIBAgIEQd8NtDANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJi
+cjETMBEGA1UEChMKSUNQLUJyYXNpbDEgMB4GA1UECxMXQ2FpeGEgRWNvbm9taWNh
+IEZlZGVyYWwxETAPBgNVBAMTCEFDIENBSVhBMB4XDTA1MDYzMDE3NDEyMloXDTEx
+MTAzMDIzNTkwMFowWjELMAkGA1UEBhMCYnIxEzARBgNVBAoTCklDUC1CcmFzaWwx
+IDAeBgNVBAsTF0NhaXhhIEVjb25vbWljYSBGZWRlcmFsMRQwEgYDVQQDEwtBQyBD
+QUlYQSBQSjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ+a0eW75H+V
+Ik7qKh1E6wXXxrs1dHA7VzWSvUwDAdRAX1cfowrD8y3lC0f4DeY9kHyiTIKss6gm
+wgUns0H0L1Dr1G2E40AFFnRt8uG3iDEXe5tlUJBL7uKSPHpfCcawWV+bZweIG3Bc
+kLnuL3y6x0iqRvtQ7QvTRqzAauyPLHGUhUa8xmXtx6I/x8vqoS8xr1lliMT2YX9y
+1bj9H0vcauVz57r6eMPjtuPlsNyvLz6XPo0Fj5V8SkP3cLOP7MqLATZnpMhLj1Qd
+n7sdMjyWB6MROQNMInjdx5Ie6zHfTAOOc4r0saCUsVfbs6gesCZ5S+YF5JeGh05X
+SkraOrJOEc8CAwEAAaOCAdkwggHVMA4GA1UdDwEB/wQEAwIBBjCBowYDVR0gBIGb
+MIGYMEoGBmBMAQIBBzBAMD4GCCsGAQUFBwIBFjJodHRwOi8vaWNwLmNhaXhhLmdv
+di5ici9yZXBvc2l0b3Jpby9kcGNhY2NhaXhhLnBkZjBKBgZgTAECAwcwQDA+Bggr
+BgEFBQcCARYyaHR0cDovL2ljcC5jYWl4YS5nb3YuYnIvcmVwb3NpdG9yaW8vZHBj
+YWNjYWl4YS5wZGYwgbMGA1UdHwSBqzCBqDBuoGygaqRoMGYxCzAJBgNVBAYTAmJy
+MRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSAwHgYDVQQLExdDYWl4YSBFY29ub21pY2Eg
+RmVkZXJhbDERMA8GA1UEAxMIQUMgQ0FJWEExDTALBgNVBAMTBENSTDEwNqA0oDKG
+MGh0dHA6Ly9pY3AuY2FpeGEuZ292LmJyL3JlcG9zaXRvcmlvL0FDQ0FJWEExLmNy
+bDAfBgNVHSMEGDAWgBQc/FX9ZPzcFsxlLOf5dAfeuksr1jAdBgNVHQ4EFgQUjkAv
+Cv4T1ao5oHZ0htO8fcfx5c8wDAYDVR0TBAUwAwEB/zAZBgkqhkiG9n0HQQAEDDAK
+GwRWNy4wAwIAgTANBgkqhkiG9w0BAQUFAAOCAQEAU9emrjVOoAZheRKpes0tjHzY
+Tk4MHGE2rp9Jgk7OCqfBY8vl6eQDLgGKIPLemhqqDJnX5o1F0zqo+jVo0sz04GHy
+j4B1KSj194fqS2/Fsauo1z9akYrqSX3tguDoEKDR0OVrUOjlNyZajEA/sUz/369F
+stBYc0SYy+n3cTWm59oKSNOJ8CjSDlgcx+Oh0e2ctQaAN/LpG7cOSPEKV5QsWLkQ
+GY6piuI31gi04O3NbacUyN7UsxCU+P1iRXbpcpFwCv//PJPAJRdqEGXvtrKNSik/
+AfP38MMXI9spydOezauvo/iv6dWrC3CuHjgvhFJJ5KqX5tua2QzINGmdlI0DcQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEODCCAyCgAwIBAgIBFTANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wNTEyMTkxMzAwMDBaFw0xMTA2MTky
+MzU5MDBaMFAxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSwwKgYD
+VQQDEyNBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgZGEgSnVzdGljYTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZdinSE3Ly323/o9QhmoXxj58rV88m8
+nNzlBPjs84dSB4nvaYTwCwZtbhk+NEbCzs8ydDL5JveYNqIP1sBtqxVz+nfEdDHV
+WdwJuPwrX+q5+zkv91C/nUDfvp6ZcKh2ufGeAdI6YjDiqiPJL8wUxOd4W/KAbejp
+6yynu6X3uUFaK4ziBEsTV5WwSnoh569KSJoDGr1Gyeo/2Di48UdAT3eEemW8l56G
+nwnXX+ZwDKwPMp4rn4nq9n+7VUxlProo+xEe3OUNe8iVXftRLjTp5nrvkXMwJ/g8
+Pu3CpsQUJgDl/GRpuUCB0Tb4c9zVVqu+DTToEytyV47VxJwLOFS76esCAwD3v6OB
+tzCBtDA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vYWNyYWl6LmljcGJyYXNpbC5n
+b3YuYnIvTENSYWNyYWl6LmNybDASBgNVHSAECzAJMAcGBWBMAQETMB0GA1UdDgQW
+BBS+OdrawXcl4K69Gs6kW5/AemD8ZTAfBgNVHSMEGDAWgBSK+vFXhBETNZBC+ldJ
+VGkNpMTwNzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQUFAAOCAQEAu22EJMIck6Q5E/piA3I26+JjChyuwU4UOWdk1GxqJ2AiI11N
+ZrjoEHc8W0gFTKZFFaywwBZYVqHvWao7iyRCDCXi4q2iVnPdA3GpMpuM69E2a5sf
+lH5Xd4VUoLpbozdio6hqjRSChX47MiIGu+B2b0DvtejdIIhkElnI2yysa0FhP7af
+qqr/FjH4Z1nDFxfJPIPQ3+u+nVICIGF7Fx/RCefIxo7+1/7e4IPsOdgCaJnsAE3x
+Kr1tz8mC+Wd8WR8ieeWwcEDt7frV1vXHSeqA8n0QwaNWfYneDWqklcr7Z9Z6bu6B
+yQfHRF6V/bSFpw6nZkYHZs7JO3w+3wmyJvc7Tg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEczCCA1ugAwIBAgISMjAwNzA3MDYxNDA5MjgwMDAxMA0GCSqGSIb3DQEBBQUA
+MFsxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMTcwNQYDVQQDEy5I
+T01BdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhIHYyMB4X
+DTA3MDcwNjE0MTI1NVoXDTE1MDIwNTEzMzMwN1owgZIxCzAJBgNVBAYTAkJSMRMw
+EQYDVQQKEwpJQ1AtQnJhc2lsMTswOQYDVQQLEzJTZXJ2aWNvIEZlZGVyYWwgZGUg
+UHJvY2Vzc2FtZW50byBkZSBEYWRvcyAtIFNFUlBSTzExMC8GA1UEAxMoSE9NQXV0
+b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIGRvIFNFUlBSTyB2MjCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAKzhCojv3vAqr0E4bYLk4622BIG2c81+AM0PJvzW
+yxlWEZ9LMc5OmAsLGCKavIA0gPCPaYOsUJmH96aJTBCbHHXhJ0axfbiDCSDYU/yl
+QnDrfaviffeMKWXo0iDqep+1CYHipdryzSjjMz+cMmDf0FIZ5U3L0oIsbMIWCFeQ
+SMQKOrIA+5qsidKg9mzAi47Lib9fUkHIxkzsc1UpZJguxG0/OL8wAAzkHTGbNTRr
+KnE+GUzAZGVGTaX8gfufR7qGTfUcbuirfGbyaEf56yv2lFdDktiI2d5uqVtYsX80
+ZnI3UQhBtD74Hz8zrZYlckokkHzr3wEgiFsWAi3Ck3w4bN8CAwC366OB+DCB9TAP
+BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFJYO9HOQfXSw7NpneWFbdw/ngZMo
+MB0GA1UdDgQWBBRrbM92NcY/uYHy1BaoSs9MpXYkuzAOBgNVHQ8BAf8EBAMCAQYw
+UAYDVR0gBEkwRzBFBgVgTAEBADA8MDoGCCsGAQUFBwIBFi5odHRwOi8vY2NkaG9t
+LnNlcnByby5nb3YuYnIvRFBDaG9tYWNyYWl6djIucGRmMEAGA1UdHwQ5MDcwNaAz
+oDGGL2h0dHA6Ly9jY2Rob20uc2VycHJvLmdvdi5ici9sY3IvaG9tYWNyYWl6djIu
+Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCYhv204f6nsoVxGRBGY9vt+wvvjmODuZ0K
+4vMBLwHGyb8Fx0l6mbT9cuns2Uq4TZiL5UMz/SUHu+4Va7XZ1I8GBAMt5nhoocsG
+Ng+YzJ80fHLNqJ6PHkdixWV+nm1/d3YzZcwQ1wXirF6dYEelH2XC4EQq2y3jLmy9
+E+BKilgdwVChviH6CIPR1LLqNhumhOQuNnA9FkVopj/jDORnNmosFkp52lx9gSyC
+xLUjEA5JbVUEl3xxIPs1pm50dZLojOjzLajtgsmOvJ9tZWe8ITuiDSvLCSuKNq6w
+KRc+qJ3NYCOudS+1hJm9qN3BPq0Q3njioQSHJQT+RoF5kmB91jiW
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFcDCCBFigAwIBAgISMjAwNzA3MDYxODU3MzYwMDAxMA0GCSqGSIb3DQEBBQUA
+MIGSMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE7MDkGA1UECxMy
+U2VydmljbyBGZWRlcmFsIGRlIFByb2Nlc3NhbWVudG8gZGUgRGFkb3MgLSBTRVJQ
+Uk8xMTAvBgNVBAMTKEhPTUF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBkbyBTRVJQ
+Uk8gdjIwHhcNMDcwNzA2MTkwNTE0WhcNMTMwNzA0MTkwNTE0WjCBqTELMAkGA1UE
+BhMCQlIxEzARBgNVBAoTCklDUC1CcmFzaWwxOzA5BgNVBAsTMlNlcnZpY28gRmVk
+ZXJhbCBkZSBQcm9jZXNzYW1lbnRvIGRlIERhZG9zIC0gU0VSUFJPMQ8wDQYDVQQL
+EwZDU1BCLTExNzA1BgNVBAMTLkhPTUF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBk
+byBTRVJQUk8gRmluYWwgdjIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQC8/uBuPe+VpJLKBiDPxEP6XEEY1pdyVgCGmdaWK/byR2bZrvvnY2SwpJWYpt0i
+Fs01utxAC6FMRURUKz9c3mM7fQSlN3rZti0l/iP3Hk+D1Fs1/xUWFTl5suWUiZT1
+HVBNQrOb9WNAQFdCpRpZqvMR0ohrRo69Uaf4nA/xwxh7cpSQM2jlu/8M75xIKf13
+vI4PnxYRcC6mgctY5Qpq9ZkT8rnfeNWm+zCNj66Eg2gW5eR0LpgyT4rdE8j7YUDb
+zybRGxan7Z4bA2+srL/wccCSTlWoJrfTo/4JjKbyljhFBfVuhZlRillIZee2VYAe
+WH18dQPPUEP7uvS5b6rBzsaTAgMA69+jggGlMIIBoTASBgNVHRMBAf8ECDAGAQH/
+AgEAMB8GA1UdIwQYMBaAFGtsz3Y1xj+5gfLUFqhKz0yldiS7MB0GA1UdDgQWBBSu
+NPOXuwWxqmzD/WxPDpEpu7Yb4jAOBgNVHQ8BAf8EBAMCAQYwgfgGA1UdIASB8DCB
+7TBNBgZgTAECARAwQzBBBggrBgEFBQcCARY1aHR0cDovL2NjZC5zZXJwcm8uZ292
+LmJyL2Fjc2VycHJvL2RvY3MvZHBjYXNlcnByby5wZGYwTQYGYEwBAgMNMEMwQQYI
+KwYBBQUHAgEWNWh0dHA6Ly9jY2Quc2VycHJvLmdvdi5ici9hY3NlcnByby9kb2Nz
+L2RwY2FzZXJwcm8ucGRmME0GBmBMAQIBBzBDMEEGCCsGAQUFBwIBFjVodHRwOi8v
+Y2NkLnNlcnByby5nb3YuYnIvYWNzZXJwcm8vZG9jcy9kcGNhc2VycHJvLnBkZjBA
+BgNVHR8EOTA3MDWgM6Axhi9odHRwOi8vY2NkaG9tLnNlcnByby5nb3YuYnIvbGNy
+L2hvbXNlcnByb3YyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAeDPepDoH81XwH2wt
+CRx4eWCo4rnG94a72OZ93cZLp5qY5XD5P93WDJ5AoNzvPYHvZgzrtFz5lrP0zlV2
+d0OhhYaqwNqHpy5w/BYsM6gYvlbIIjDWdQVqc/p854YTRE9q81aVIuM2NYEFqPnC
+hTAb+sI4z3jMrrtYOBHB0XHPCzPYAKe2kP5HVKQ5LZsTY0m3A9RCWEAHaBPDQ/LX
+BYOFRqt7Yra0z22ul3TZyM/tL9jmLJyRoQC07gqjYJwgUwAa+smrGfQqSyzu9q6o
+ttLYJVsqu8G3MiTrIf6ND7TwlvxwPJAj3m+PXSdnVkBD1NKVMk9BiTeqoI7A0k6B
+XaHuQg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEOzCCAyOgAwIBAgISMjAwNzAyMDcxMzMyNTcwMDAwMA0GCSqGSIb3DQEBBQUA
+MFsxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMTcwNQYDVQQDEy5I
+T01BdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhIHYyMB4X
+DTA3MDIwNzEzMzMwN1oXDTE1MDIwNTEzMzMwN1owWzELMAkGA1UEBhMCQlIxEzAR
+BgNVBAoTCklDUC1CcmFzaWwxNzA1BgNVBAMTLkhPTUF1dG9yaWRhZGUgQ2VydGlm
+aWNhZG9yYSBSYWl6IEJyYXNpbGVpcmEgdjIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDtov6OzoFyLGNs6CmLZ9FI0FOortZY3I5crKPFX9pWEDFbxjXb
+ARn1tBhraqBOaoNMHk2jktURRE+d4NBz9FzTJqzxx7gM9mgTAVy4E9Yybc0cKH34
+6GO3AZLz9Kok6BQ/0wIVpcoyaPSoJvFV3bbOZ8tvAjIbkKjcVmVKzThL6YmOu+Zc
+EFDKoau8lk8CifERoST5c0EtRlpeX9vIYsLq4h/N6jaSnCooqVcoVQxzcGrEEmVH
+rLdplBsFW0syKI46mWvPmubHL3MZseX5uOGl0N7aT6Obi7XAKDsSMcOEB7C1Kx56
+vRg0pE5p0oRgX0x0wjgpP6jVx3SJhM1snPEjAgMAtdWjgfgwgfUwDwYDVR0TAQH/
+BAUwAwEB/zAfBgNVHSMEGDAWgBSWDvRzkH10sOzaZ3lhW3cP54GTKDAdBgNVHQ4E
+FgQUlg70c5B9dLDs2md5YVt3D+eBkygwDgYDVR0PAQH/BAQDAgEGMFAGA1UdIARJ
+MEcwRQYFYEwBAQAwPDA6BggrBgEFBQcCARYuaHR0cDovL2NjZGhvbS5zZXJwcm8u
+Z292LmJyL0RQQ2hvbWFjcmFpenYyLnBkZjBABgNVHR8EOTA3MDWgM6Axhi9odHRw
+Oi8vY2NkaG9tLnNlcnByby5nb3YuYnIvbGNyL2hvbWFjcmFpenYyLmNybDANBgkq
+hkiG9w0BAQUFAAOCAQEAoysgdsBf8fwZn9Ldd27BjXyvMTU+z3Yxz4Y+iNzW+zf6
+kDVeU9rjbOFfHpNRRp/sK35tRjG2+wqdk1qXRggg7i/lj8wWYMkgGmw2PFc6IxgI
+ZsHCa8IGGGETpc94ZTF527zHvqL58pnxIh+5K/+8/YyoRCHlH7jqtZxopUFRqEup
+FqRTeBoqwy0RE1Gr6txhdktUJqOtY7xw9fg5tskbaReiWSYxkI2nWkIKcQ7W3wMc
+WI6PzTAM8nTB5kzBs8E9z7EFWAOvTrf6tbB6xhDPW0wEpp6BWF8zqBvCqlM8cwOV
+1CshiU0615IBiD6DuuS66FtoYpOR0aKwFCMVJMIWOA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBBjANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxNDAyBgNVBAMTK0F1
+dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBSYWl6IEJyYXNpbGVpcmEgdjEwHhcNMDkw
+MjE4MTkwMzI4WhcNMTkwMjE4MTkwMzI4WjBPMQswCQYDVQQGEwJCUjETMBEGA1UE
+ChMKSUNQLUJyYXNpbDErMCkGA1UEAxMiQXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3Jh
+IFNFUlBSTyB2MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKvqKR6c
+rhBNjIB6amZnpOE+cuSfmrpG9tO6qTlFBUrhgJdsXreUxsQzuKGJ9C0TvjP9kNOE
+AXAc6kdueDb82kQNOworuqJi9uY2NiNgQSIIp/5/4HQ4bNldcBB9gPjNHW9lk4gn
+GJCwQ6mie3H6WQOwkIh7lWKWs5Fpx4H+qspLJ3blTou3AFQYDS6i4dvwGY58R517
+6Xl3B3jsJ5OifWzJr4pMxK3TJrzZ6jsdEslbRPeRSafTkF3eqLr6nrv619r34ISd
+7NCj8eFGb6yYb+OuE7gPuxQq/1lxQyXigj+pN94X+8KC4fT7xxr300BnHZCBlaw9
+T4ivr0Y/RaB3DW8CAwEAAaOBuzCBuDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
+/wQEAwIBBjAUBgNVHSAEDTALMAkGBWBMAQECMAAwPwYDVR0fBDgwNjA0oDKgMIYu
+aHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFjcmFpenYxLmNybDAf
+BgNVHSMEGDAWgBRCsixcdAEHvpv/VTM77im7XZG/BjAdBgNVHQ4EFgQUHVcev4hY
+sn4YKPjAk1SFyxJpKQwwDQYJKoZIhvcNAQEFBQADggEBAG1MKQN2MRTgm4rUSCmy
+CWnFozVoMMAUC6n4NnDvFIeBZOvsQueD0banmc8JwzvWweQUhXW/2YhE5h63hHve
+p71ie1CHRqncLfgfooBvjJBTdXbHsg+k3sqmqt84ndG/YbeOsQ64+ovK1yBjrzen
+Z0ZmLKsrP2cHS7yeaJb/ZlhcAQ9jhxqN+jKT3BSvAss4LJ3Nh0cEr+bQaaN3FA7I
+fsbLCg9JZQ5WeAL3f894ERW3Iur+IzZvDfQj/I/+D1wiHIRLuYSkMV5gswyqUDSj
+btMn/D1SMp5QsR6n3qfaoS66jbQgZglQJ6rvt4HFIyEx4rJgIhtv72UeMQWcIyrX
+XNo=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBPMQswCQYDVQQGEwJCUjET
+MBEGA1UEChMKSUNQLUJyYXNpbDErMCkGA1UEAxMiQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFNFUlBSTyB2MjAeFw0wOTAzMTIxNTM0MjZaFw0xNjAzMTIxNTM0MjZa
+MIGmMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE7MDkGA1UECxMy
+U2VydmljbyBGZWRlcmFsIGRlIFByb2Nlc3NhbWVudG8gZGUgRGFkb3MgLSBTRVJQ
+Uk8xDzANBgNVBAsTBkNTUEItMTE0MDIGA1UEAxMrQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIGRvIFNFUlBSTyBGaW5hbCB2MjCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOD6O5umiLCPP2GK18RjAmpzpnjPvetQBvOeQtc61SCVvxxnkYnD
+nWVtHbRLQwnuV/IXuiP7HdnBUV5E8lvNJwGxSzyAGYa1rIYlBeoT1hrFCQkL5csd
+yq8Rp2CiFSjE2CrVSx0g1fhS0e1Hu53bWEMC38i4RPCjG91qW9zQT8p8et8nIJ2H
+By4JC+bRm2u7kkXx4QlCL2eCX6+Q+Q95uRuUd40oO8400gCrPz49jzW/eO9G2EKL
+22GRFOQkKAMmtQZFPu8Sh+U7DOoqyO46VdamHY6uPmQoyJoq+KFuWUkKHy99fORY
+7CXWB3O+CqQYIetHGvG+pA7HAQlWAgsO3CsCAwDRi6OCAacwggGjMA4GA1UdDwEB
+/wQEAwIBBjCB/gYDVR0gBIH2MIHzME8GBmBMAQIBEDBFMEMGCCsGAQUFBwIBFjdo
+dHRwczovL2NjZC5zZXJwcm8uZ292LmJyL2Fjc2VycHJvL2RvY3MvZHBjYWNzZXJw
+cm8ucGRmME8GBmBMAQIDDTBFMEMGCCsGAQUFBwIBFjdodHRwczovL2NjZC5zZXJw
+cm8uZ292LmJyL2Fjc2VycHJvL2RvY3MvZHBjYWNzZXJwcm8ucGRmME8GBmBMAQIB
+ETBFMEMGCCsGAQUFBwIBFjdodHRwczovL2NjZC5zZXJwcm8uZ292LmJyL2Fjc2Vy
+cHJvL2RvY3MvZHBjYWNzZXJwcm8ucGRmMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6
+Ly9jY2Quc2VycHJvLmdvdi5ici9sY3IvYWNzZXJwcm92Mi5jcmwwHwYDVR0jBBgw
+FoAUHVcev4hYsn4YKPjAk1SFyxJpKQwwHQYDVR0OBBYEFMMHIUz2AP2ET2ip5rAF
+/d+wpak8MBIGA1UdEwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcNAQEFBQADggEBADGN
+NtiN/Tk3bRBAaq6H1ipgyq4IPeA4F9wYLm9lYsoAqvHYV6vYQ5CuZs24lpidashd
+z4E+qdjWDW40eucfreWPD4Y6PbfUwuABS7vbyuAPn78VTODc/jUxj6k5sqEuzDa0
+tOLLggUdI5TnL8k163ezAfTvBYF/PgqQSKLPl7ygNVwIglkDMI17fcuiKNPwP6bm
+jpyaiVt0Z9q9zqkJuEmgeFI5deiVuotjBTmfSsqOfcIKtHChNvKodkSnXWJfV/Qr
+JDVdzjr2fUAjh9xmUj8jy/P00Go48o7XchMwCRxNdNHuKlzaqMSyCw5943ot9MQR
+br8utaD7Mh3XSUi7qj4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEEDCCAvigAwIBAgIBBTANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxNDAyBgNVBAMTK0F1
+dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBSYWl6IEJyYXNpbGVpcmEgdjEwHhcNMDgx
+MDI0MTIzOTQ2WhcNMTgxMDI0MTIzOTQ2WjBXMQswCQYDVQQGEwJCUjETMBEGA1UE
+ChMKSUNQLUJyYXNpbDEzMDEGA1UEAxMqQUMgU2VjcmV0YXJpYSBkYSBSZWNlaXRh
+IEZlZGVyYWwgZG8gQnJhc2lsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEA3o1phpOoNH/BA5ydDgeDndk7mItSVUK7t3PNjCkF/7lCSM3SiQxi/XsnvS3f
+4NCR5Vq/4yGrWa31FJi/XYRlLZH46L86S2hOBkV5sIxrr0EPN5q/EUTQCrcbcqgr
+V9QX2SSPqOO5+wS8+zygNfBAOPFSLpqwyzSYS+ZQdeDkuhFMikF07e7XwfRvc+/K
+eR0RU1p8GijtAtqsXGmq/8wunlySd7g+h2CfkILnY87GyB3UwOP7zSOaV2Un9/yz
+dBRduGpUZSx2yn8FrBB7mCfE58gJo5YZ1KZfcSfRNrAaQW6B7mDLsRnDdn5jYQnn
+0w/xf9Kwq3YFB1rZKRDBGLqhVwIDAQABo4GlMIGiMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgEGMD8GA1UdHwQ4MDYwNKAyoDCGLmh0dHA6Ly9hY3JhaXou
+aWNwYnJhc2lsLmdvdi5ici9MQ1JhY3JhaXp2MS5jcmwwHwYDVR0jBBgwFoAUQrIs
+XHQBB76b/1UzO+4pu12RvwYwHQYDVR0OBBYEFBtdm3eNVCVtBaPCcqy/d/mxPDhL
+MA0GCSqGSIb3DQEBBQUAA4IBAQAfE0z7q9dicmpuK8TbkmFjb5NdR4xxy5n2H+hI
+S/mxAXXUxD1MHQltmFzjGjCVLKxfnLXDFPzfEuB1KCEACseAS1q2iAgDaEOAbVoG
+iSNZF5Tx8wCdkGXszBC8kdM4RUsLqVAAIkNxH+6K7I9r9Z5/8oNg/JZHCnNP5BLq
+1nKxplltISTTfNecUP2bY/7f24oGlYfoYwtzPZEDu1Zw01jHbT5K4vaMG4pV81d6
+exb+paeCVcZItAo8lxc35ago9RG5HyWO0Ep08CVzesO5CNVhFb5BQDKO/fDDyNPH
+DirDKnx6ZJAclztk6KtIJK9yjixYkBeG6J78eYMhnxJJmfRh
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEtTCCA52gAwIBAgIBCTANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJCUjET
+MBEGA1UEChMKSUNQLUJyYXNpbDEzMDEGA1UEAxMqQUMgU2VjcmV0YXJpYSBkYSBS
+ZWNlaXRhIEZlZGVyYWwgZG8gQnJhc2lsMB4XDTA4MTEwODEyMzQzOFoXDTE2MTEw
+ODEyMzQzOFowgYoxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMTYw
+NAYDVQQLEy1TZWNyZXRhcmlhIGRhIFJlY2VpdGEgRmVkZXJhbCBkbyBCcmFzaWwg
+LSBSRkIxLjAsBgNVBAMTJUF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBkbyBTRVJQ
+Uk9SRkIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD4D53Epar0R3dq
+xNeJK+G72gYfnIYiBprbiqqewZjev34EThrlGzZ2clWaBZBsnzEW6+LqHXXSJfOn
+IWN3FJ768OQwC09N+k6DX7wt7QRAlkulKptfIXdpjjrSmjoUngcctaiKCuT46kYx
+Pk0Mx0BfIJ4vfwMPE8xd9zkdFknWR+UqDS+gKbApxHQbSrVEjD5fU3R7S6impF8s
+rlev7aKBxw3Yd6Uyz/eaxis3i6cBvwnuF+ZojI/hLPqHpFXnqsFAVzTCxpCSTG25
+ChL52vgaX35AQU7zm9NRvoOOgRgSRdyMj+piaSt7iwU79VCbiQu4knjsSyySbpVG
+bGAvLCOjAgMAovWjggFWMIIBUjAOBgNVHQ8BAf8EBAMCAQYwgacGA1UdIASBnzCB
+nDBMBgZgTAECAQowQjBABggrBgEFBQcCARY0aHR0cDovL3d3dy5yZWNlaXRhLmZh
+emVuZGEuZ292LmJyL2FjcmZiL2RwY2FjcmZiLnBkZjBMBgZgTAECAwQwQjBABggr
+BgEFBQcCARY0aHR0cDovL3d3dy5yZWNlaXRhLmZhemVuZGEuZ292LmJyL2FjcmZi
+L2RwY2FjcmZiLnBkZjBCBgNVHR8EOzA5MDegNaAzhjFodHRwOi8vd3d3LnJlY2Vp
+dGEuZmF6ZW5kYS5nb3YuYnIvYWNyZmIvYWNyZmIuY3JsMB8GA1UdIwQYMBaAFBtd
+m3eNVCVtBaPCcqy/d/mxPDhLMB0GA1UdDgQWBBS5IouGJEbnoq3nKTuMaDtNrXSR
+FDASBgNVHRMBAf8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4IBAQC5EgiFEaa8
+QmYfcdmsTEjKf3xHIkNbFrJMXFPufUy7RADAsWNj2osjiS0flQehuyPwuKObUQUY
+dXNArO9hZogXQO3NEEFX81KS0CHlT2s+Wn43q5cshoQODfL6msjca+le8LqvDZht
+deGuODxfkp9t43kBvNt8P54b/9ADBuWqY1303j22l6t+vJd9Mdw8ZVnhf7MCLa5x
+4TEr1iRi0mZxE3im9S9DgkhgYDBUPxu16h31fSokgdo4flxbrsqvxitlX0pOqsE2
+KcASqg27iTq2Nfy9pQMC4T4ro/vQwpyWccQAfPNLmroJxlp2XaKaUkSGMVsmhaZT
+kB+UOMPGrRky
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEODCCAyCgAwIBAgIBFTANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wNTEyMTkxMzAwMDBaFw0xMTA2MTky
+MzU5MDBaMFAxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSwwKgYD
+VQQDEyNBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgZGEgSnVzdGljYTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMZdinSE3Ly323/o9QhmoXxj58rV88m8
+nNzlBPjs84dSB4nvaYTwCwZtbhk+NEbCzs8ydDL5JveYNqIP1sBtqxVz+nfEdDHV
+WdwJuPwrX+q5+zkv91C/nUDfvp6ZcKh2ufGeAdI6YjDiqiPJL8wUxOd4W/KAbejp
+6yynu6X3uUFaK4ziBEsTV5WwSnoh569KSJoDGr1Gyeo/2Di48UdAT3eEemW8l56G
+nwnXX+ZwDKwPMp4rn4nq9n+7VUxlProo+xEe3OUNe8iVXftRLjTp5nrvkXMwJ/g8
+Pu3CpsQUJgDl/GRpuUCB0Tb4c9zVVqu+DTToEytyV47VxJwLOFS76esCAwD3v6OB
+tzCBtDA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vYWNyYWl6LmljcGJyYXNpbC5n
+b3YuYnIvTENSYWNyYWl6LmNybDASBgNVHSAECzAJMAcGBWBMAQETMB0GA1UdDgQW
+BBS+OdrawXcl4K69Gs6kW5/AemD8ZTAfBgNVHSMEGDAWgBSK+vFXhBETNZBC+ldJ
+VGkNpMTwNzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
+9w0BAQUFAAOCAQEAu22EJMIck6Q5E/piA3I26+JjChyuwU4UOWdk1GxqJ2AiI11N
+ZrjoEHc8W0gFTKZFFaywwBZYVqHvWao7iyRCDCXi4q2iVnPdA3GpMpuM69E2a5sf
+lH5Xd4VUoLpbozdio6hqjRSChX47MiIGu+B2b0DvtejdIIhkElnI2yysa0FhP7af
+qqr/FjH4Z1nDFxfJPIPQ3+u+nVICIGF7Fx/RCefIxo7+1/7e4IPsOdgCaJnsAE3x
+Kr1tz8mC+Wd8WR8ieeWwcEDt7frV1vXHSeqA8n0QwaNWfYneDWqklcr7Z9Z6bu6B
+yQfHRF6V/bSFpw6nZkYHZs7JO3w+3wmyJvc7Tg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIErDCCA5SgAwIBAgISMjAwNjAzMTAxNDUwNTgwMDAxMA0GCSqGSIb3DQEBBQUA
+MFAxCzAJBgNVBAYTAkJSMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSwwKgYDVQQDEyNB
+dXRvcmlkYWRlIENlcnRpZmljYWRvcmEgZGEgSnVzdGljYTAeFw0wNjAzMTAxNTAz
+MDhaFw0xMTA1MzExNTAzMDhaMIGHMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQ
+LUJyYXNpbDE1MDMGA1UECxMsQXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIGRhIEp1
+c3RpY2EgLSBBQy1KVVMxLDAqBgNVBAMTI0F1dG9yaWRhZGUgQ2VydGlmaWNhZG9y
+YSBTRVJQUk8tSlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyn3a
+XFTTnHlmeTGqzo9NQIUezNQhqfmTUvk0t03KWWZ6gmXO48sO9O/j4jmTWes0rztl
+5hS617gICjUq5KkcowB6L18MQiVPnMmG+Wp1+qNT3fWKsOXrWGPfvtHavVRJ9E1X
+XCt4e5207aaJPKCCWiQ9KDl3Z7+NoSx5yffnpwHHN/6SelrXaRelQlmZZG2gFK2l
+3INF+mCkJ+RKN9FTTjMpog5BNXQHDbCgJQvyp6J4UDs5UfHNjxPQneEa+31/wCWe
+FKHB23mwMtA/IPkifLlKy+rb+Yx6Q/Z962/g6bKKo5XvNXqi1lBm3sqvh6Ztz0HR
+GMzveM0C97nH4lxjxQIDAM3To4IBRjCCAUIwEgYDVR0TAQH/BAgwBgEB/wIBADAi
+BgNVHSMBAQAEGDAWgBS+OdrawXcl4K69Gs6kW5/AemD8ZTAgBgNVHQ4BAQAEFgQU
+5XNjTkp4mXa8Lfj/+Byo/ytxGj0wDgYDVR0PAQH/BAQDAgEGMIGWBgNVHSABAQAE
+gYswgYgwQgYGYEwBAgEVMDgwNgYIKwYBBQUHAgEWKmh0dHA6Ly93d3cuYWNqdXMu
+Z292LmJyL2FjanVzL2RwY2FjanVzLnBkZjBCBgZgTAECAxIwODA2BggrBgEFBQcC
+ARYqaHR0cDovL3d3dy5hY2p1cy5nb3YuYnIvYWNqdXMvZHBjYWNqdXMucGRmMD0G
+A1UdHwEBAAQzMDEwL6AtoCuGKWh0dHA6Ly93d3cuYWNqdXMuZ292LmJyL2FjanVz
+L2FjanVzdjEuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC2b7GzutyRxCuLRu+oTVrW
+r0h+AFmpTXibRyATf3llBImJgQpJMAJHjRUZI7jRexUJKAqlRfO32f/Ozdd8BV+f
+kzc6E0yGB4Stnqg2Qh2uMu1F5ado9I/uwPEAUoieDxtygxEMJHI3+8vGdUelo6Of
+cufmLmuRFiLf+QdpE53MjXYfMMgCa60ngVfhlOmzTx8AIwjf/VTwk5Ojo0DZUQIQ
+uOzWIKkLVNSqQq/NTTib5tO7eRpXGjhhgNJDwa/HDnY7Bihf6YWaxSM5A1GoIVCi
+ueMWldKJdBikQ0GeyqElqmmEJba0LoJ2OlqnkXs0RZ+akp53hfIW1RCnYQqJfouM
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIETjCCAzagAwIBAgIBCTANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIxEzARBgNV
+BAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25hbCBkZSBUZWNub2xv
+Z2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJyYXNpbGlhMQswCQYDVQQIEwJE
+RjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAe
+Fw0wMjEwMTQxNjM4MDBaFw0wNzEwMTQyMzU5MDBaMGYxCzAJBgNVBAYTAkJSMRMwEQYDVQQK
+EwpJQ1AtQnJhc2lsMUIwQAYDVQQDEzlBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgZGEgU2Vj
+cmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC+z/4Dqlb6MwAikElPMur5MPnpmeGYlG2lnqL9zp81FJlf8TcBnQ4Qm934cTmuYsjL
+i3H8qZkHKd5KWV7pw1IfY1fv2jN62mGCT0Y0QDLO/Ovc8VR/dF89xQC9A0/s/5eXgCX6QwbM
+Q/46d0kQxz2WrDcx4qtGShfsvgW1kbHa9+KJWKM8WG513eyDPpN7FcD8MXmNKGkE+eorUtwZ
+YcSI7Ooe5xjBxRPemqz6fmCZ7HsXi4udNpGAFxV+KNW3gYuKIiGZ3Ry4JDMz9/gnBYPOrfL0
+e6qOa45oas1pcw93Eg2F3igNR324a6BK51sIwvUroihVE8RxrdP9FTw+7WTrAgMBAAGjgbcw
+gbQwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xD
+UmFjcmFpei5jcmwwEgYDVR0gBAswCTAHBgVgTAEBCDAdBgNVHQ4EFgQUrUPpEW3CB3EnV7nS
+vxCCVYg9EHcwHwYDVR0jBBgwFoAUivrxV4QREzWQQvpXSVRpDaTE8DcwDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADUrc/zWTEEXMYEgO/AI
+lCFD5AWxE0qh80ysKgR0x+W1ULkYwSxM6sZrth3mzb4iIJk/KclqGns94kTLx6e4zFZpi2oK
+mltgkkTKMHjR6hanMMdGwJ1/jZL6APye0SmyL57rof0cM6k5d1reERYbj4HEpUSZlo2WZ5uk
+EfolqJwznssbROIlReQnpThk3Om5GVNJnblgEFFmUhcLoM+IY0Y0spORu938skMEGzECW8+o
+D93t4CCw7Dylho1gPVME85elx3OzQU56JfA5luCcrGXikbrKDTA/0Cy/JrRRn1nPfHXh1C2X
+ocL9SUmEVCNjMLAZL32jf3TTKk68dx6bqB4=
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIID7jCCAtagAwIBAgIBATANBgkqhkiG9w0BAQUFADBPMQswCQYDVQQGEwJCUjEM
+MAoGA1UEChMDSUNQMTIwMAYDVQQDEylBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEg
+UmFpeiBIb21vbG9nYWNhbzAeFw0wOTAxMDMxMjIxNTJaFw0yMjAxMDQxMjIxNTJa
+ME8xCzAJBgNVBAYTAkJSMQwwCgYDVQQKEwNJQ1AxMjAwBgNVBAMTKUF1dG9yaWRh
+ZGUgQ2VydGlmaWNhZG9yYSBSYWl6IEhvbW9sb2dhY2FvMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAtNolXLj/KExEvuz+vZDsfKSTfIYM+x9CaN71/Awf
+SpB+ghegALAiW0LaJojSRdA1GCZwce4RThtt/zjrsuMxUnFL74Jpu8RMcp7Gc/Us
+MO8fdfaY6vn+PX/XDaUyEk36R6hRrp0ywe1VwAaXmhWjWvDll+MYH7hSHguRb/Od
+44CjAlHVfA61rU3sOD6HDbnK9mjyzB5a0mN8DZgq2NcaGZywXlVQLtec/p6jbqw7
+OJLiDh+eS+jvf8A8OkMCQ82ZabPD2yp8xK4NWwvO+QdwuscdgOm1FsEeUoJJu9sl
+alXXzEyzSHHtl5rZirzUAIcwDEUT+JXtD1808BxpTbw4xQIDAQABo4HUMIHRMFEG
+A1UdIARKMEgwRgYGYEwBAoNEMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly9jY2Rob20u
+c2VycHJvLmdvdi5ici9kb2MvZHBjcmFpemhvbS5wZGYwPAYDVR0fBDUwMzAxoC+g
+LYYraHR0cDovL2NjZGhvbS5zZXJwcm8uZ292LmJyL2xjci9yYWl6aG9tLmNybDAd
+BgNVHQ4EFgQUtfP6QrSAZIeIicDPi2G3Af1Gdi4wDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAJenILQO0B7vSlNocewH
+0n/vgqeVT5+fuda+/GuQX+lN4wwR1E3+Hwq1IIVdTLPS6luRhvDkJkQ+rA65rJA8
++/1DZHAaz5qAajmkY3rzu2Vf5PGKfKqGIG7ejdYFQnQrURvX3LSlubb3odSQ6jYs
+4gEbhrnFTyAk5XmOm/QJ62ME7V3OqXOnEf3YyeX9fTKwvvTIhdBR1CNXjDGLhnly
+b8cLoRKwGKVQMdX9EsX8mYezOT600YBmVU0NNT95DeH6Zn4hZjgMPUI//3r1Jmxa
+f8YFOGMjwkNXNOY5t8ZTrrwBCrS8spJQ8DCO9l7wb1FzC2Gg5E3AdJW3mRky85vz
+qWA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDuzCCAqOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBPMQswCQYDVQQGEwJCUjEM
+MAoGA1UEChMDSUNQMTIwMAYDVQQDEylBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEg
+UmFpeiBIb21vbG9nYWNhbzAeFw0wOTAxMDMxNjAwMDlaFw0yMDAxMDMxNjAwMDla
+MFgxCzAJBgNVBAYTAkJSMQwwCgYDVQQKEwNJQ1AxOzA5BgNVBAMTMkF1dG9yaWRh
+ZGUgQ2VydGlmaWNhZG9yYSBJbnRlcm1lZGlhcmlhIEhvbW9sb2dhY2FvMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6VtI2zmzIqaNBls8f7p70Cu2WopJ
+c647RzxkqgIOKhkk67/UEkKYm3ZY1YmK1Y1k3iIpU0+upzH2/ohCo+OOCGAoucsI
+2NWMOb7XRkY7eO9wtD+ErBul/3dGb7feWVnIo4CYoFKmui4VgedV0XlXQoyi1PRA
+2NG+oSMEYyBfwenBppgbHJtAo898eRnx7ZGR/hhdojSw5JXF9AIjUuX2PyjZIXDa
+b6iIsIQ8SB1yMuZOuqGKdHZg0KXTYjJBkAhkoGtSxB+ikIdjNX9vmkUYAYB0kkcr
+X/iIc39yW/dGispzliAGcqXL7XCHxZjkyqvRJhBMxqQ0HCfKMix0trKD+QIDAQAB
+o4GYMIGVMB0GA1UdDgQWBBTWARCTrzdL6Khlw/VGYhjxdqma0zAPBgNVHRMBAf8E
+BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAVBgNVHSAEDjAMMAoGBmBMAQKDRTAAMDwG
+A1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jY2Rob20uc2VycHJvLmdvdi5ici9sY3Iv
+cmFpemhvbS5jcmwwDQYJKoZIhvcNAQEFBQADggEBADrg4jMKCAMgrGB9xqIMDwjg
+Yk0/p4xnqcCI1rGPyLK9e4upvcS5pwoxrlxwCI1RNTFltqOEmaT0thh2yrnD1BIX
+UNvX/OVV+RFnf0jAmEJ+D2iIDz+YVb+rMYxo7C9Xz0kvSzuQfZW/TbMHoV5PWh87
+6uPykYcoAzDtm3+sPbKMSO5FIijdfTxSbPyezQ4D1gs6FHBULeCXLx2lteHr5YTA
+glG8C3S1eJfWjVdF/Z+waEOSJmNshGPOKtIqn4oYr48H36hwNJVs4e+twAJoYJy/
+Z26h2241MRpxXymhfnkdb8Wa7ciAki9+37Pta2rjRXzzcSnSxsNUpIFP62rpfGs=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEUzCCAzugAwIBAgIBBTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJCUjEM
+MAoGA1UEChMDSUNQMTswOQYDVQQDEzJBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEg
+SW50ZXJtZWRpYXJpYSBIb21vbG9nYWNhbzAeFw0wOTAyMTExNjMwMzhaFw0xMjAy
+MTExNjMwMzhaMIGHMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDEs
+MCoGA1UECxMjU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgLSBTUkYxNTAz
+BgNVBAMTLEhPTUF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBkbyBTRVJQUk8gU1JG
+IHYyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxgxQn/v4kkJhrueW
+UcGRYwLN+6qC3gWD1rEpQK994Rmq5Ne7QJUsLasV9BLUT7Be5isEZucUBxr/aRIe
+UCcAbfpDAuOS8R9ZphxxX4UCUlOkIap1NZNoojbmI4dLhOSPcrTDIm2MGvpq93/p
+mvfpzz2iazYft8ZNXoxJx255EWvirWJGnv2WlYtiv7NOuj4fwb88dc4g6jSTw6eu
+HbpbNJ+MEs91eVioC4EPO1GrxJuAb9KFAISQxZoBRyQuQdOUMMY6XTg4SxizUFbZ
+/FdI8JD30unA8aE8hrTW/ACdsOSad5eRFNdWJ+jlz/7Ohc5aj69TR2KKy/TWnQXF
+IBx3eQIDAL7lo4H3MIH0MA4GA1UdDwEB/wQEAwIBBjBIBgNVHSAEQTA/MD0GBWBM
+AQIyMDQwMgYIKwYBBQUHAgEWJmh0dHA6Ly9jY2Rob20uc2VycHJvLmdvdi4vZG9j
+cy9kcGMucGRmMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jY2Rob20uc2VycHJv
+Lmdvdi5ici9sY3IvYWNpbnRlcm1lZGlhcmlhLmNybDAfBgNVHSMEGDAWgBTWARCT
+rzdL6Khlw/VGYhjxdqma0zAdBgNVHQ4EFgQUh4z1W51V52V8KRZMAx5UD/VbMa8w
+EgYDVR0TAQH/BAgwBgEB/wIBADANBgkqhkiG9w0BAQUFAAOCAQEAHo7RjG9nP+oV
+cLyLfQsMBY+M6OuIj+nVaRcd/tPxKuUXKMK+o7wEL3Qzc0j6p7F4ThbtDA1IFZBq
+BgULyVRIp5iQY26kXC85A/jIu/c6KfPQ02IByU89rlXjVVnHjMHO1BNfQDJMe+XJ
+XTpBSBp15+Y63him9o3qhKsQKWsZll2FhvuT4phOYFGvOULt693nJzqmcqMM37lg
+0m1Fhurrf3ngfQglpXuWFoU/3qV/7300Chs9YZG/sZn8/959INEp//s3Awovkmgy
+0GlL80gxFbbOh7oqaEAkbLZ2CgMKliwhlN0QtlBSG2bEOQgwRvyuQngTnJ/okERH
+EOonzgne4Q==
+-----END CERTIFICATE-----
Index: /branches/2.0/security/distribuicao/gerador_conf.py
===================================================================
--- /branches/2.0/security/distribuicao/gerador_conf.py	(revision 1182)
+++ /branches/2.0/security/distribuicao/gerador_conf.py	(revision 1182)
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+#Arquivo com configurqcao do gerador
+# url para o projeto no subversion, pode ser informado via parametro "url="
+#projeto = 'http://expressosvn.pae.serpro/expresso/trunk/source'
+
+# Path para onde o conteudo(arquivos) do projeto serao copiados. Tambem pode ser informado 
+# via linha de comando, parametro "caminho=" .
+#caminho = 'expresso'
+
+#sufixo = 'expresso-serpro-r'
+
+# Nome da maquina para onde sera transferido o arquivo ".tar" via FTP.
+# Pode ser informado via linha de comando, parametro "ftp="
+#s_ftp = 'xxxservidor_ftp'
+#s_path = 'upload/Expresso-Serpro'
+
+# Nome da pasta de trabalho. Nesta pasta sao geradas subpastas para cada distribuicao.
+trabalho = 'trabalho'
+pasta_do_produto = 'expresso'
+
+# parametros. Cada distribuiÃ§Ã£o esta configurada neste dicionario....
+parametros = {'Serpro':{'projeto':'http://expressosvn.pae.serpro/expresso/trunk/source',
+                        'caminho':'expressoserpro',
+                        'sufixo':'expresso-serpro-r',
+                        's_ftp':'xservidor_ftp',
+                        's_path':'upload/Expresso-Serpro',
+                        'pasta_do_produto':'expresso',
+                        'script':''},
+              'Comunidade':{'projeto':'http://expressosvn.pae.serpro/expressocomunidade/trunk',
+                        'caminho':'expressocomunidade',
+                        'sufixo':'expresso-comunidade-r',
+                        's_ftp':'servidor_ftp',
+                        's_path':'upload/Expresso-Comunidade',
+                        'pasta_do_produto':'expresso',
+                        'script':''}}
+
+
Index: /branches/2.0/security/distribuicao/gerador
===================================================================
--- /branches/2.0/security/distribuicao/gerador	(revision 1182)
+++ /branches/2.0/security/distribuicao/gerador	(revision 1182)
@@ -0,0 +1,350 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import os, popen2, fcntl, select, sys, os, pysvn, time
+# Prerequisitos: cliente do subversion instalado, e pysvn mais o python-dialog instalado no python.
+from dialog import *
+
+#FunÃ§Ã£o para solicitar ao usuario o tipo de distribuicao
+def mostra(d,escolhas):
+   return d.menu("Selecione o tipo de distrbuicao desejada:",height=0,choices=escolhas)
+
+def msg_box_info(d,msg):
+    d.infobox(msg,height=19,width=60)
+    time.sleep(2)
+    return
+
+def msg_box_sim_nao(d,msg):
+    return d.yesno(msg,height=18,width=80)
+
+
+#As duas funcoes, a seguir, executam comandos e retornam resultado.
+def makeNonBlocking(fd):
+    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+    try:
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY)
+    except AttributeError:
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.FNDELAY)
+
+def getCommandOutput(command):
+    f=sys.stdout
+    child = popen2.Popen3(command, 1) # Captura stdout e stderr do comando
+    child.tochild.close( )             # nao necessitamos escrever para a  stdin do processo iniciado pelo comando
+    outfile = child.fromchild
+    outfd = outfile.fileno( )
+    errfile = child.childerr
+    errfd = errfile.fileno( )
+    makeNonBlocking(outfd)            # Evitamos deadlocks tornando fd's nonblock.
+    makeNonBlocking(errfd)
+    outdata, errdata = [  ], [  ]
+    outeof = erreof = False
+    while True:
+        f.write('.') 
+        f.flush()
+        to_check = [outfd]*(not outeof) + [errfd]*(not erreof)
+        ready = select.select(to_check, [  ], [  ]) # Espera por dados...
+        if outfd in ready[0]:
+            outchunk = outfile.read( )
+            if outchunk == '':
+                outeof = True
+            else:
+                outdata.append(outchunk)
+        if errfd in ready[0]:
+            errchunk = errfile.read( )
+            if errchunk == '':
+                erreof = True
+            else:
+                errdata.append(errchunk)
+        if outeof and erreof:
+            break
+        select.select([  ],[  ],[  ],.1) # Seta um intervalo de tempo bem curto para os buffers serem preenchidos.
+    err = child.wait( )
+    if err != 0:
+        raise RuntimeError, '%r falhou com codigo %d\n%s' % (
+            command, err, ''.join(errdata))
+    return ''.join(outdata)
+
+
+#Callback para login no svn
+def Login_no_svn( realm, username, may_save ):
+	from getpass import getpass
+	global flag
+	print 'Por favor, identifique-se'
+	usuario = raw_input('Usuario:').strip()
+	senha = getpass('Senha:').strip()
+	if not flag:
+		flag = True
+	else:
+		flag = False
+		print 'Falhou acesso ao Subversion...\n'
+		sys.exit(92)
+	return True, usuario, senha, True
+
+#Rotina para recuperar dados do svn sobre as revisoes e gerar arquivo html
+def r_svn_to_html():
+	global projeto, caminho,rev
+	arq_sai = caminho + os.sep + 'revisoes-svn.php'
+	saida=''
+	cor =''
+	linha = 0	
+	
+	try:
+		cliente = pysvn.Client()
+		cliente.callback_get_login = Login_no_svn
+		logs = cliente.log( projeto, revision_start=pysvn.Revision( pysvn.opt_revision_kind.number,rev),
+			revision_end=pysvn.Revision( pysvn.opt_revision_kind.number, 0 ),
+			discover_changed_paths=True,strict_node_history=True,limit=0 )
+	except:
+		print 'Erro Obtendo dados do svn para gerar revisoes-svn.php'
+		sys.exit(93)	  
+
+	fd = open(arq_sai, 'w')
+	saida += '<?php ?>\n'
+	saida += '<div>\n'
+	saida += '<h3 align="center" style="color: #0000FF;margin-bottom: 0 ">Expresso - Subversion</h3>\n'
+	saida += '<table border="1" witdh="100%">\n'
+	saida += '<tr bgcolor="#D3DCE3">\n'
+	saida += '<td>Revis&atilde;o</td><td><font color="#000066">Data</font></td><td><font color="#000066">Implementa&ccedil;&atilde;o</font></td></tr>\n'
+	for revisao in logs:
+		linha += 1
+		if linha%2:
+			cor = '#DDDDDD'
+		else:
+			cor = '#EEEEEE'	
+		saida += '<tr bgcolor="'+cor+'">\n'	
+		saida += '<td valign="top">'+str(revisao['revision'].number)+'</td>\n'
+		saida += '<td valign="top">'+time.strftime('%Y-%m-%d  %H:%M:%S',time.localtime(revisao['date']))+'</td>\n'
+		try:
+			aux = revisao['message']
+			saida += '<td valign="top">'+aux+'</td>\n'
+		except:
+			saida += '<td valign="top">Nao foi possivel obter a msg no subversion. Falha no encode...</td>\n'
+		saida += '</tr>\n'
+	saida += '</table>\n'
+	saida += '</div>\n'	
+	fd.write(saida.encode('iso8859','replace'))
+
+	fd.close()
+
+# ##########################################################################################
+#                                     INICIO
+# ##########################################################################################
+
+#Carrega arquivo com dados que configuram as distribuiÃ§oes possÃ­veis...
+from gerador_conf import *
+
+
+# Pega identificador de cada distribuicao configurada...
+aux_tipo_distribuicao = parametros.keys()
+
+# Ordena
+aux_tipo_distribuicao.sort()
+
+tipo_distribuicao = {}
+
+# Tipo de distribuicao: uma entrada para distribuicao configurada...
+for i in range(0,len(aux_tipo_distribuicao)):
+	tipo_distribuicao[str(i+1)] = aux_tipo_distribuicao[i]
+
+
+# ordena pelo enumerador.....
+Ks = tipo_distribuicao.keys()
+Ks.sort()
+escolhas = []
+
+# COnstruindo itens do menu, uma entrada para cada distribuicao configurada...
+for t in Ks:
+     escolhas.append([t,tipo_distribuicao[t]]) 
+
+flag = False
+rev=''
+params={}
+
+d=Dialog()
+# Seta titulo geral....
+d.add_persistent_args(["--backtitle", "Gera distribuicao"])
+
+# Usuario seleciona a distribuiÃ§Ã£o desejada.
+# cod = 0 , usuario selecionou um item, ver valor em opc...
+# cod = 1 , usuario selecionou cancelar, valor em opc Ã© vazio
+# cod = 2 , usurio usou a tecla esc, valor em opc Ã© vazio
+[cod,opc] = mostra(d,escolhas)
+
+# Se opc for vazio o usuario pediu para cancelar, ou usou a tecla esc...
+if opc == '':
+  sys.exit(98)
+
+# Com base na selecao do usuario, carrega variaveis com valÃŽres configurados....	  
+projeto = parametros[tipo_distribuicao[opc]]['projeto']
+caminho = parametros[tipo_distribuicao[opc]]['caminho']
+s_ftp = parametros[tipo_distribuicao[opc]]['s_ftp']
+s_path = parametros[tipo_distribuicao[opc]]['s_path']
+sufixo = parametros[tipo_distribuicao[opc]]['sufixo']
+pasta_do_produto =  parametros[tipo_distribuicao[opc]]['pasta_do_produto']
+script = parametros[tipo_distribuicao[opc]]['script']
+
+# Seta titulo da distribuicao que sera efetuada...
+d.add_persistent_args(["--title", 'DistribuiÃ§Ã£o ' +tipo_distribuicao[opc]])
+
+# Pega lista de parametros, se houver....
+if len(sys.argv)>1:
+	for parametro in sys.argv:
+		aux=parametro.split('=')
+		if len(aux)==2:
+			params[aux[0]] = aux[1]
+if 'url' in params.keys():
+	projeto = params['url']
+	#Se existir o parametro projeto usar o ultimo elemento para compor o caminho no dir corrente.
+	aux  = projeto.split(os.sep)
+	caminho=os.getcwd()+os.sep+aux[-1]
+if 'caminho' in params.keys():
+	caminho = params['caminho']
+if 'rev' in params.keys():
+	try:
+		rev = int(params['rev'])	
+	except:
+		msg_box_info(d,"Revisao invalida.")
+		sys.exit(99)
+
+
+# Prepara alerta ao usuario, para confirmar execuÃ§Ã£o...
+if len(params)<2:
+	msg = "\nExemplo: ."+os.sep+"gerador.py url=http://xxx/bbb/yyyy caminho="+os.sep+"xxxx"+os.sep+"pasta rev=xxx\n"
+	msg = msg + "          url :     Aponta  projeto no svn\n"
+	msg = msg + "          caminho : Aponta o destino dos arquivo do projeto.\n" 
+	msg = msg + "          rev : Informa o num. da revisao que sera tratada. Se vazio trata a mais recente.\n" 	
+	msg = msg + "\nAssumindo valores default para esta execucao."
+msg = msg + "Sera processado o projeto apontado pela url " + projeto + "\n"
+if rev != '':
+    msg = msg + "Posicionado na revisao " + str(rev) + ".\n\n"
+else:
+    msg = msg + "\n\n"
+msg = msg + "Os arquivos do projeto serao armazenados no caminho " + caminho + "\n\n"
+msg = msg + "Continuar?"
+
+# Envia msg ao usuario e agurada resposta....
+if msg_box_sim_nao(d,msg) != d.DIALOG_OK:
+	sys.exit(98)
+
+
+# Iniciando o processamento...
+caminho1 = trabalho + os.sep + caminho
+caminho = trabalho + os.sep + caminho + os.sep + pasta_do_produto
+
+# Verifica a existencia de uma pasta de trabalho. 
+# Nesta pasta serao gerados subdiretorios que vao conter as distribuiÃ§oes.
+if not os.path.isdir(trabalho):
+        os.makedirs(trabalho)
+
+if os.path.isdir(caminho1):
+        retorno  = getCommandOutput('rm -fR ' + caminho1)
+else:
+        retorno  = getCommandOutput('mkdir ' + caminho1)
+
+try:
+	cliente = pysvn.Client()
+	cliente.callback_get_login = Login_no_svn
+except:
+	msg_box_info(d,'\nErro gerando cliente do subversion.')
+	sys.exit(97)
+	
+
+if rev == '':
+	try:
+		msg_box_info(d,'\nAguarde.\nVerificando qual a revisao mais recente do projeto no Subversion.\n')
+		logs = cliente.log( projeto, discover_changed_paths=True,strict_node_history=True,limit=1)
+		rev = logs[0]['revision'].number
+	except:
+		msg_box_info(d,'\nErro determinando qual he a revisao mais recente no subversion.')
+		sys.exit(96)
+
+msg = '\nBuscando os arquivos da revisao ' + str(rev) + '.\n\n'
+msg = msg + 'Esta atividade pode demorar alguns minutos. \nAguarde.\n'
+msg_box_info(d,msg)
+
+try:
+	revisao = cliente.export( projeto,
+		caminho,
+		force=False,
+		revision=pysvn.Revision(pysvn.opt_revision_kind.number,rev),
+		native_eol=None,
+		ignore_externals=False,
+		recurse=True,
+		peg_revision=pysvn.Revision(pysvn.opt_revision_kind.number, rev))
+
+except:
+	msg_box_info(d,'\nErro buscando arquivos no subversion.')
+	sys.exit(95)	
+	
+#Gera arqivo com infrmacoes do Subversion
+r_svn_to_html()
+
+# Se existir um nome de script no arquivo de configuraÃ§Ã£o, vai tentar executar...
+if script != '':
+   msg = msg + "\nExecutando o script ." + os.sep + script + " \n"
+   msg_box_info(d,msg) 
+   retorno  = getCommandOutput('.' + os.sep + script)
+
+#Solicita execucao do comando de compactaÃ§ao
+path_corrente = os.getcwd()
+os.chdir(caminho1)
+retorno  = getCommandOutput("tar -zcf " + sufixo + str(rev) + ".tgz ./expresso")
+os.chdir(path_corrente)
+msg = ''
+msg = msg + "\nFoi gerado o arquivo " + sufixo + str(rev) + ".tgz \n"
+msg_box_info(d,msg)
+
+#Faz ftp para a maquina de distribuicao...
+msg = msg +  'Iniciando copia do arquivo gerado para a maquina de distribuicao(FTP)\n'
+msg_box_info(d,msg)
+from ftplib import FTP
+servidor_ftp = s_ftp
+try:
+	ftp = FTP(servidor_ftp,'anonymous','')
+except:
+        msg = msg + '\nErro ao conectar no servidor FTP.'
+	msg_box_info(d,msg)
+	sys.exit(94)
+
+resp = ftp.cwd(s_path)
+msg = msg + resp + '\n'
+msg_box_info(d,msg)
+lista = ftp.nlst()
+if 'R'+str(rev) in lista:
+	ftp.cwd('R'+str(rev))
+        # pode existir a pasta mas nao o arquivo....
+        try:
+	   resp = ftp.delete(sufixo+str(rev)+'.tgz')
+        except:
+           pass
+	msg = msg + resp + '\n'
+        msg_box_info(d,msg)
+	resp = ftp.cwd('../')
+	msg = msg + resp + '\n'
+        msg_box_info(d,msg)
+else:
+        resp = ftp.mkd('R'+str(rev))
+        msg = msg + resp + '\n'
+        msg_box_info(d,msg)
+resp = ftp.cwd('R'+str(rev))
+msg = msg + resp + '\n'
+msg_box_info(d,msg)
+f = open(caminho1 + os.sep + sufixo+str(rev)+'.tgz','rb')
+resp = ftp.storbinary('STOR ' + sufixo + str(rev) + '.tgz',f)
+msg = msg + resp + '\n'
+msg_box_info(d,msg)
+msg = msg + 'Verifique os dados abaixo:\n'
+msg_box_info(d,msg)
+resp = ftp.pwd()
+msg = msg + 'Diretorio de distribuicao R'+str(rev)+' gerado em '+servidor_ftp+' : '+resp 
+msg_box_info(d,msg)
+resp = ftp.retrlines('LIST')
+msg = msg + '\nConteudo gerado no diretorio acima : \n'+resp+'\n'
+msg_box_info(d,msg)
+ftp.close()
+msg = msg + '\nProcessamento concluido.\n'	
+msg_box_info(d,msg)	
+	
+	
+	
+	
Index: /branches/2.0/security/distribuicao/dialog.py
===================================================================
--- /branches/2.0/security/distribuicao/dialog.py	(revision 1182)
+++ /branches/2.0/security/distribuicao/dialog.py	(revision 1182)
@@ -0,0 +1,1571 @@
+# dialog.py --- A python interface to the Linux "dialog" utility
+# Copyright (C) 2000  Robb Shecter, Sultanbek Tezadov
+# Copyright (C) 2002, 2003, 2004  Florent Rougon
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Python interface to dialog-like programs.
+
+This module provides a Python interface to dialog-like programs such
+as `dialog', `Xdialog' and `whiptail'.
+
+It provides a Dialog class that retains some parameters such as the
+program name and path as well as the values to pass as DIALOG*
+environment variables to the chosen program.
+
+For a quick start, you should look at the demo.py file that comes
+with pythondialog. It demonstrates a simple use of each widget
+offered by the Dialog class.
+
+See the Dialog class documentation for general usage information,
+list of available widgets and ways to pass options to dialog.
+
+
+Notable exceptions
+------------------
+
+Here is the hierarchy of notable exceptions raised by this module:
+
+  error
+     ExecutableNotFound
+     BadPythonDialogUsage
+     PythonDialogSystemError
+        PythonDialogIOError
+        PythonDialogOSError
+        PythonDialogErrorBeforeExecInChildProcess
+        PythonDialogReModuleError
+     UnexpectedDialogOutput
+     DialogTerminatedBySignal
+     DialogError
+     UnableToCreateTemporaryDirectory
+     PythonDialogBug
+     ProbablyPythonBug
+
+As you can see, every exception `exc' among them verifies:
+
+  issubclass(exc, error)
+
+so if you don't need fine-grained error handling, simply catch
+`error' (which will probably be accessible as dialog.error from your
+program) and you should be safe.
+
+"""
+
+from __future__ import nested_scopes
+import sys, os, tempfile, random, string, re, types
+
+
+# Python < 2.3 compatibility
+if sys.hexversion < 0x02030000:
+    # The assignments would work with Python >= 2.3 but then, pydoc
+    # shows them in the DATA section of the module...
+    True = 0 == 0
+    False = 0 == 1
+
+
+# Exceptions raised by this module
+#
+# When adding, suppressing, renaming exceptions or changing their
+# hierarchy, don't forget to update the module's docstring.
+class error(Exception):
+    """Base class for exceptions in pythondialog."""
+    def __init__(self, message=None):
+        self.message = message
+    def __str__(self):
+        return "<%s: %s>" % (self.__class__.__name__, self.message)
+    def complete_message(self):
+        if self.message:
+            return "%s: %s" % (self.ExceptionShortDescription, self.message)
+        else:
+            return "%s" % self.ExceptionShortDescription
+    ExceptionShortDescription = "pythondialog generic exception"
+
+# For backward-compatibility
+#
+# Note: this exception was not documented (only the specific ones were), so
+#       the backward-compatibility binding could be removed relatively easily.
+PythonDialogException = error
+
+class ExecutableNotFound(error):
+    """Exception raised when the dialog executable can't be found."""
+    ExceptionShortDescription = "Executable not found"
+
+class PythonDialogBug(error):
+    """Exception raised when pythondialog finds a bug in his own code."""
+    ExceptionShortDescription = "Bug in pythondialog"
+
+# Yeah, the "Probably" makes it look a bit ugly, but:
+#   - this is more accurate
+#   - this avoids a potential clash with an eventual PythonBug built-in
+#     exception in the Python interpreter...
+class ProbablyPythonBug(error):
+    """Exception raised when pythondialog behaves in a way that seems to \
+indicate a Python bug."""
+    ExceptionShortDescription = "Bug in python, probably"
+
+class BadPythonDialogUsage(error):
+    """Exception raised when pythondialog is used in an incorrect way."""
+    ExceptionShortDescription = "Invalid use of pythondialog"
+
+class PythonDialogSystemError(error):
+    """Exception raised when pythondialog cannot perform a "system \
+operation" (e.g., a system call) that should work in "normal" situations.
+
+    This is a convenience exception: PythonDialogIOError, PythonDialogOSError
+    and PythonDialogErrorBeforeExecInChildProcess all derive from this
+    exception. As a consequence, watching for PythonDialogSystemError instead
+    of the aformentioned exceptions is enough if you don't need precise
+    details about these kinds of errors.
+
+    Don't confuse this exception with Python's builtin SystemError
+    exception.
+
+    """
+    ExceptionShortDescription = "System error"
+    
+class PythonDialogIOError(PythonDialogSystemError):
+    """Exception raised when pythondialog catches an IOError exception that \
+should be passed to the calling program."""
+    ExceptionShortDescription = "IO error"
+
+class PythonDialogOSError(PythonDialogSystemError):
+    """Exception raised when pythondialog catches an OSError exception that \
+should be passed to the calling program."""
+    ExceptionShortDescription = "OS error"
+
+class PythonDialogErrorBeforeExecInChildProcess(PythonDialogSystemError):
+    """Exception raised when an exception is caught in a child process \
+before the exec sytem call (included).
+
+    This can happen in uncomfortable situations like when the system is out
+    of memory or when the maximum number of open file descriptors has been
+    reached. This can also happen if the dialog-like program was removed
+    (or if it is has been made non-executable) between the time we found it
+    with _find_in_path and the time the exec system call attempted to
+    execute it...
+
+    """
+    ExceptionShortDescription = "Error in a child process before the exec " \
+                                "system call"
+
+class PythonDialogReModuleError(PythonDialogSystemError):
+    """Exception raised when pythondialog catches a re.error exception."""
+    ExceptionShortDescription = "'re' module error"
+
+class UnexpectedDialogOutput(error):
+    """Exception raised when the dialog-like program returns something not \
+expected by pythondialog."""
+    ExceptionShortDescription = "Unexpected dialog output"
+
+class DialogTerminatedBySignal(error):
+    """Exception raised when the dialog-like program is terminated by a \
+signal."""
+    ExceptionShortDescription = "dialog-like terminated by a signal"
+
+class DialogError(error):
+    """Exception raised when the dialog-like program exits with the \
+code indicating an error."""
+    ExceptionShortDescription = "dialog-like terminated due to an error"
+
+class UnableToCreateTemporaryDirectory(error):
+    """Exception raised when we cannot create a temporary directory."""
+    ExceptionShortDescription = "unable to create a temporary directory"
+
+# Values accepted for checklists
+try:
+    _on_rec = re.compile(r"on", re.IGNORECASE)
+    _off_rec = re.compile(r"off", re.IGNORECASE)
+
+    _calendar_date_rec = re.compile(
+        r"(?P<day>\d\d)/(?P<month>\d\d)/(?P<year>\d\d\d\d)$")
+    _timebox_time_rec = re.compile(
+        r"(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)$")
+except re.error, v:
+    raise PythonDialogReModuleError(v)
+
+
+# This dictionary allows us to write the dialog common options in a Pythonic
+# way (e.g. dialog_instance.checklist(args, ..., title="Foo", no_shadow=1)).
+#
+# Options such as --separate-output should obviously not be set by the user
+# since they affect the parsing of dialog's output:
+_common_args_syntax = {
+    "aspect": lambda ratio: ("--aspect", str(ratio)),
+    "backtitle": lambda backtitle: ("--backtitle", backtitle),
+    "beep": lambda enable: _simple_option("--beep", enable),
+    "beep_after": lambda enable: _simple_option("--beep-after", enable),
+    # Warning: order = y, x!
+    "begin": lambda coords: ("--begin", str(coords[0]), str(coords[1])),
+    "cancel": lambda string: ("--cancel-label", string),
+    "clear": lambda enable: _simple_option("--clear", enable),
+    "cr_wrap": lambda enable: _simple_option("--cr-wrap", enable),
+    "create_rc": lambda file: ("--create-rc", file),
+    "defaultno": lambda enable: _simple_option("--defaultno", enable),
+    "default_item": lambda string: ("--default-item", string),
+    "help": lambda enable: _simple_option("--help", enable),
+    "help_button": lambda enable: _simple_option("--help-button", enable),
+    "help_label": lambda string: ("--help-label", string),
+    "ignore": lambda enable: _simple_option("--ignore", enable),
+    "item_help": lambda enable: _simple_option("--item-help", enable),
+    "max_input": lambda size: ("--max-input", str(size)),
+    "no_kill": lambda enable: _simple_option("--no-kill", enable),
+    "no_cancel": lambda enable: _simple_option("--no-cancel", enable),
+    "nocancel": lambda enable: _simple_option("--nocancel", enable),
+    "no_shadow": lambda enable: _simple_option("--no-shadow", enable),
+    "ok_label": lambda string: ("--ok-label", string),
+    "print_maxsize": lambda enable: _simple_option("--print-maxsize",
+                                                   enable),
+    "print_size": lambda enable: _simple_option("--print-size", enable),
+    "print_version": lambda enable: _simple_option("--print-version",
+                                                   enable),
+    "separate_output": lambda enable: _simple_option("--separate-output",
+                                                     enable),
+    "separate_widget": lambda string: ("--separate-widget", string),
+    "shadow": lambda enable: _simple_option("--shadow", enable),
+    "size_err": lambda enable: _simple_option("--size-err", enable),
+    "sleep": lambda secs: ("--sleep", str(secs)),
+    "stderr": lambda enable: _simple_option("--stderr", enable),
+    "stdout": lambda enable: _simple_option("--stdout", enable),
+    "tab_correct": lambda enable: _simple_option("--tab-correct", enable),
+    "tab_len": lambda n: ("--tab-len", str(n)),
+    "timeout": lambda secs: ("--timeout", str(secs)),
+    "title": lambda title: ("--title", title),
+    "trim": lambda enable: _simple_option("--trim", enable),
+    "version": lambda enable: _simple_option("--version", enable)}
+    
+
+def _simple_option(option, enable):
+    """Turn on or off the simplest dialog Common Options."""
+    if enable:
+        return (option,)
+    else:
+        # This will not add any argument to the command line
+        return ()
+
+
+def _find_in_path(prog_name):
+    """Search an executable in the PATH.
+
+    If PATH is not defined, the default path ":/bin:/usr/bin" is
+    used.
+
+    Return a path to the file or None if no readable and executable
+    file is found.
+
+    Notable exception: PythonDialogOSError
+
+    """
+    try:
+        # Note that the leading empty component in the default value for PATH
+        # could lead to the returned path not being absolute.
+        PATH = os.getenv("PATH", ":/bin:/usr/bin") # see the execvp(3) man page
+        for dir in string.split(PATH, ":"):
+            file_path = os.path.join(dir, prog_name)
+            if os.path.isfile(file_path) \
+               and os.access(file_path, os.R_OK | os.X_OK):
+                return file_path
+        return None
+    except os.error, v:
+        raise PythonDialogOSError(v.strerror)
+
+
+def _path_to_executable(f):
+    """Find a path to an executable.
+
+    Find a path to an executable, using the same rules as the POSIX
+    exec*p functions (see execvp(3) for instance).
+
+    If `f' contains a '/', it is assumed to be a path and is simply
+    checked for read and write permissions; otherwise, it is looked
+    for according to the contents of the PATH environment variable,
+    which defaults to ":/bin:/usr/bin" if unset.
+
+    The returned path is not necessarily absolute.
+
+    Notable exceptions:
+
+        ExecutableNotFound
+        PythonDialogOSError
+        
+    """
+    try:
+        if '/' in f:
+            if os.path.isfile(f) and \
+                   os.access(f, os.R_OK | os.X_OK):
+                res = f
+            else:
+                raise ExecutableNotFound("%s cannot be read and executed" % f)
+        else:
+            res = _find_in_path(f)
+            if res is None:
+                raise ExecutableNotFound(
+                    "can't find the executable for the dialog-like "
+                    "program")
+    except os.error, v:
+        raise PythonDialogOSError(v.strerror)
+
+    return res
+
+
+def _to_onoff(val):
+    """Convert boolean expressions to "on" or "off"
+
+    This function converts every non-zero integer as well as "on",
+    "ON", "On" and "oN" to "on" and converts 0, "off", "OFF", etc. to
+    "off".
+
+    Notable exceptions:
+
+        PythonDialogReModuleError
+        BadPythonDialogUsage
+
+    """
+    if type(val) == types.IntType:
+        if val:
+            return "on"
+        else:
+            return "off"
+    elif type(val) == types.StringType:
+        try:
+            if _on_rec.match(val):
+                return "on"
+            elif _off_rec.match(val):
+                return "off"
+        except re.error, v:
+            raise PythonDialogReModuleError(v)
+    else:
+        raise BadPythonDialogUsage("invalid boolean value: %s" % val)
+
+
+def _compute_common_args(dict):
+    """Compute the list of arguments for dialog common options.
+
+    Compute a list of the command-line arguments to pass to dialog
+    from a keyword arguments dictionary for options listed as "common
+    options" in the manual page for dialog. These are the options
+    that are not tied to a particular widget.
+
+    This allows to specify these options in a pythonic way, such as:
+
+       d.checklist(<usual arguments for a checklist>,
+                   title="...",
+                   backtitle="...")
+
+    instead of having to pass them with strings like "--title foo" or
+    "--backtitle bar".
+
+    Notable exceptions: None
+
+    """
+    args = []
+    for key in dict.keys():
+        args.extend(_common_args_syntax[key](dict[key]))
+    return args
+
+
+def _create_temporary_directory():
+    """Create a temporary directory (securely).
+
+    Return the directory path.
+
+    Notable exceptions:
+        - UnableToCreateTemporaryDirectory
+        - PythonDialogOSError
+        - exceptions raised by the tempfile module (which are
+          unfortunately not mentioned in its documentation, at
+          least in Python 2.3.3...)
+
+    """
+    find_temporary_nb_attempts = 5
+    for i in range(find_temporary_nb_attempts):
+        try:
+            # Using something >= 2**31 causes an error in Python 2.2...
+            tmp_dir = os.path.join(tempfile.gettempdir(),
+                                   "%s-%u" \
+                                   % ("pythondialog",
+                                      random.randint(0, 2**30-1)))
+        except os.error, v:
+            raise PythonDialogOSError(v.strerror)
+
+        try:
+            os.mkdir(tmp_dir, 0700)
+        except os.error:
+            continue
+        else:
+            break
+    else:
+        raise UnableToCreateTemporaryDirectory(
+            "somebody may be trying to attack us")
+
+    return tmp_dir
+
+
+# DIALOG_OK, DIALOG_CANCEL, etc. are environment variables controlling
+# dialog's exit status in the corresponding situation.
+#
+# Note:
+#    - 127 must not be used for any of the DIALOG_* values. It is used
+#      when a failure occurs in the child process before it exec()s
+#      dialog (where "before" includes a potential exec() failure).
+#    - 126 is also used (although in presumably rare situations).
+_dialog_exit_status_vars = { "OK": 0,
+                             "CANCEL": 1,
+                             "ESC": 2,
+                             "ERROR": 3,
+                             "EXTRA": 4,
+                             "HELP": 5 }
+
+
+# Main class of the module
+class Dialog:
+
+    """Class providing bindings for dialog-compatible programs.
+
+    This class allows you to invoke dialog or a compatible program in
+    a pythonic way to build quicky and easily simple but nice text
+    interfaces.
+
+    An application typically creates one instance of the Dialog class
+    and uses it for all its widgets, but it is possible to use
+    concurrently several instances of this class with different
+    parameters (such as the background title) if you have the need
+    for this.
+
+    The exit code (exit status) returned by dialog is to be
+    compared with the DIALOG_OK, DIALOG_CANCEL, DIALOG_ESC,
+    DIALOG_ERROR, DIALOG_EXTRA and DIALOG_HELP attributes of the
+    Dialog instance (they are integers).
+
+    Note: although this class does all it can to allow the caller to
+          differentiate between the various reasons that caused a
+          dialog box to be closed, its backend, dialog 0.9a-20020309a
+          for my tests, doesn't always return DIALOG_ESC when the
+          user presses the ESC key, but often returns DIALOG_ERROR
+          instead. The exit codes returned by the corresponding
+          Dialog methods are of course just as wrong in these cases.
+          You've been warned.
+
+
+    Public methods of the Dialog class (mainly widgets)
+    ---------------------------------------------------
+
+    The Dialog class has the following methods:
+
+    add_persistent_args
+    calendar
+    checklist
+    fselect
+
+    gauge_start
+    gauge_update
+    gauge_stop
+
+    infobox
+    inputbox
+    menu
+    msgbox
+    passwordbox
+    radiolist
+    scrollbox
+    tailbox
+    textbox
+    timebox
+    yesno
+
+    clear                 (obsolete)
+    setBackgroundTitle    (obsolete)
+
+
+    Passing dialog "Common Options"
+    -------------------------------
+
+    Every widget method has a **kwargs argument allowing you to pass
+    dialog so-called Common Options (see the dialog(1) manual page)
+    to dialog for this widget call. For instance, if `d' is a Dialog
+    instance, you can write:
+
+      d.checklist(args, ..., title="A Great Title", no_shadow=1)
+
+    The no_shadow option is worth looking at:
+
+      1. It is an option that takes no argument as far as dialog is
+         concerned (unlike the "--title" option, for instance). When
+         you list it as a keyword argument, the option is really
+         passed to dialog only if the value you gave it evaluates to
+         true, e.g. "no_shadow=1" will cause "--no-shadow" to be
+         passed to dialog whereas "no_shadow=0" will cause this
+         option not to be passed to dialog at all.
+
+      2. It is an option that has a hyphen (-) in its name, which you
+         must change into an underscore (_) to pass it as a Python
+         keyword argument. Therefore, "--no-shadow" is passed by
+         giving a "no_shadow=1" keyword argument to a Dialog method
+         (the leading two dashes are also consistently removed).
+
+
+    Exceptions
+    ----------
+
+    Please refer to the specific methods' docstrings or simply to the
+    module's docstring for a list of all exceptions that might be
+    raised by this class' methods.
+
+    """
+
+    def __init__(self, dialog="dialog", DIALOGRC=None, compat="dialog",
+                 use_stdout=None):
+        """Constructor for Dialog instances.
+
+        dialog   -- name of (or path to) the dialog-like program to
+                    use; if it contains a '/', it is assumed to be a
+                    path and is used as is; otherwise, it is looked
+                    for according to the contents of the PATH
+                    environment variable, which defaults to
+                    ":/bin:/usr/bin" if unset.
+        DIALOGRC -- string to pass to the dialog-like program as the
+                    DIALOGRC environment variable, or None if no
+                    modification to the environment regarding this
+                    variable should be done in the call to the
+                    dialog-like program
+        compat   -- compatibility mode (see below)
+
+        The officially supported dialog-like program in pythondialog
+        is the well-known dialog program written in C, based on the
+        ncurses library. It is also known as cdialog and its home
+        page is currently (2004-03-15) located at:
+
+            http://dickey.his.com/dialog/dialog.html
+
+        If you want to use a different program such as Xdialog, you
+        should indicate the executable file name with the `dialog'
+        argument *and* the compatibility type that you think it
+        conforms to with the `compat' argument. Currently, `compat'
+        can be either "dialog" (for dialog; this is the default) or
+        "Xdialog" (for, well, Xdialog).
+
+        The `compat' argument allows me to cope with minor
+        differences in behaviour between the various programs
+        implementing the dialog interface (not the text or graphical
+        interface, I mean the "API"). However, having to support
+        various APIs simultaneously is a bit ugly and I would really
+        prefer you to report bugs to the relevant maintainers when
+        you find incompatibilities with dialog. This is for the
+        benefit of pretty much everyone that relies on the dialog
+        interface.
+
+        Notable exceptions:
+
+            ExecutableNotFound
+            PythonDialogOSError
+
+        """        
+        # DIALOGRC differs from the other DIALOG* variables in that:
+        #   1. It should be a string if not None
+        #   2. We may very well want it to be unset
+        if DIALOGRC is not None:
+            self.DIALOGRC = DIALOGRC
+
+        # After reflexion, I think DIALOG_OK, DIALOG_CANCEL, etc.
+        # should never have been instance attributes (I cannot see a
+        # reason why the user would want to change their values or
+        # even read them), but it is a bit late, now. So, we set them
+        # based on the (global) _dialog_exit_status_vars.keys.
+        for var in _dialog_exit_status_vars.keys():
+            varname = "DIALOG_" + var
+            setattr(self, varname, _dialog_exit_status_vars[var])
+
+        self._dialog_prg = _path_to_executable(dialog)
+        self.compat = compat
+        self.dialog_persistent_arglist = []
+
+        # Use stderr or stdout?
+        if self.compat == "Xdialog":
+            # Default to stdout if Xdialog
+            self.use_stdout = True
+        else:
+            self.use_stdout = False
+        if use_stdout != None:
+            # Allow explicit setting
+            self.use_stdout = use_stdout
+        if self.use_stdout:
+            self.add_persistent_args(["--stdout"])
+
+    def add_persistent_args(self, arglist):
+        self.dialog_persistent_arglist.extend(arglist)
+
+    # For compatibility with the old dialog...
+    def setBackgroundTitle(self, text):
+        """Set the background title for dialog.
+
+        This method is obsolete. Please remove calls to it from your
+        programs.
+
+	"""
+	self.add_persistent_args(("--backtitle", text))
+
+    def _call_program(self, redirect_child_stdin, cmdargs, **kwargs):
+	"""Do the actual work of invoking the dialog-like program.
+
+        Communication with the dialog-like program is performed
+        through one or two pipes, depending on
+        `redirect_child_stdin'. There is always one pipe that is
+        created to allow the parent process to read what dialog
+        writes on its standard error stream.
+        
+        If `redirect_child_stdin' is True, an additional pipe is
+        created whose reading end is connected to dialog's standard
+        input. This is used by the gauge widget to feed data to
+        dialog.
+
+        Beware when interpreting the return value: the length of the
+        returned tuple depends on `redirect_child_stdin'.
+
+        Notable exception: PythonDialogOSError (if pipe() or close()
+                           system calls fail...)
+
+        """
+        # We want to define DIALOG_OK, DIALOG_CANCEL, etc. in the
+        # environment of the child process so that we know (and
+        # even control) the possible dialog exit statuses.
+        new_environ = {}
+        new_environ.update(os.environ)
+        for var in _dialog_exit_status_vars:
+            varname = "DIALOG_" + var
+            new_environ[varname] = str(getattr(self, varname))
+        if hasattr(self, "DIALOGRC"):
+            new_environ["DIALOGRC"] = self.DIALOGRC
+
+        # Create:
+        #   - a pipe so that the parent process can read dialog's output on
+        #     stdout/stderr
+        #   - a pipe so that the parent process can feed data to dialog's
+        #     stdin (this is needed for the gauge widget) if
+        #     redirect_child_stdin is True
+        try:
+            # rfd = File Descriptor for Reading
+            # wfd = File Descriptor for Writing
+            (child_rfd, child_wfd) = os.pipe()
+            if redirect_child_stdin:
+                (child_stdin_rfd,  child_stdin_wfd)  = os.pipe()
+        except os.error, v:
+            raise PythonDialogOSError(v.strerror)
+
+        child_pid = os.fork()
+        if child_pid == 0:
+            # We are in the child process. We MUST NOT raise any exception.
+            try:
+                # The child process doesn't need these file descriptors
+                os.close(child_rfd)
+                if redirect_child_stdin:
+                    os.close(child_stdin_wfd)
+                # We want:
+                #   - dialog's output on stderr/stdout to go to child_wfd
+                #   - data written to child_stdin_wfd to go to dialog's stdin
+                #     if redirect_child_stdin is True
+                if self.use_stdout:
+                    os.dup2(child_wfd, 1)
+                else:
+                    os.dup2(child_wfd, 2)
+                if redirect_child_stdin:
+                    os.dup2(child_stdin_rfd, 0)
+
+                arglist = [self._dialog_prg] + \
+                          self.dialog_persistent_arglist + \
+                          _compute_common_args(kwargs) + \
+                          cmdargs
+                # Insert here the contents of the DEBUGGING file if you want
+                # to obtain a handy string of the complete command line with
+                # arguments quoted for the shell and environment variables
+                # set.
+                os.execve(self._dialog_prg, arglist, new_environ)
+            except:
+                os._exit(127)
+
+            # Should not happen unless there is a bug in Python
+            os._exit(126)
+
+        # We are in the father process.
+        #
+        # It is essential to close child_wfd, otherwise we will never
+        # see EOF while reading on child_rfd and the parent process
+        # will block forever on the read() call.
+        # [ after the fork(), the "reference count" of child_wfd from
+        #   the operating system's point of view is 2; after the child exits,
+        #   it is 1 until the father closes it itself; then it is 0 and a read
+        #   on child_rfd encounters EOF once all the remaining data in
+        #   the pipe has been read. ]
+        try:
+            os.close(child_wfd)
+            if redirect_child_stdin:
+                os.close(child_stdin_rfd)
+                return (child_pid, child_rfd, child_stdin_wfd)
+            else:
+                return (child_pid, child_rfd)
+        except os.error, v:
+            raise PythonDialogOSError(v.strerror)
+
+    def _wait_for_program_termination(self, child_pid, child_rfd):
+        """Wait for a dialog-like process to terminate.
+
+        This function waits for the specified process to terminate,
+        raises the appropriate exceptions in case of abnormal
+        termination and returns the exit status and standard error
+        output of the process as a tuple: (exit_code, stderr_string).
+
+        `child_rfd' must be the file descriptor for the
+        reading end of the pipe created by self._call_program()
+        whose writing end was connected by self._call_program() to
+        the child process's standard error.
+
+        This function reads the process's output on standard error
+        from `child_rfd' and closes this file descriptor once
+        this is done.
+
+        Notable exceptions:
+
+            DialogTerminatedBySignal
+            DialogError
+            PythonDialogErrorBeforeExecInChildProcess
+            PythonDialogIOError
+            PythonDialogBug
+            ProbablyPythonBug
+
+        """
+        exit_info = os.waitpid(child_pid, 0)[1]
+        if os.WIFEXITED(exit_info):
+            exit_code = os.WEXITSTATUS(exit_info)
+        # As we wait()ed for the child process to terminate, there is no
+        # need to call os.WIFSTOPPED()
+        elif os.WIFSIGNALED(exit_info):
+            raise DialogTerminatedBySignal("the dialog-like program was "
+                                           "terminated by signal %u" %
+                                           os.WTERMSIG(exit_info))
+        else:
+            raise PythonDialogBug("please report this bug to the "
+                                  "pythondialog maintainers")
+
+        if exit_code == self.DIALOG_ERROR:
+            raise DialogError("the dialog-like program exited with "
+                              "code %d (was passed to it as the DIALOG_ERROR "
+                              "environment variable)" % exit_code)
+        elif exit_code == 127:
+            raise PythonDialogErrorBeforeExecInChildProcess(
+                "perhaps the dialog-like program could not be executed; "
+                "perhaps the system is out of memory; perhaps the maximum "
+                "number of open file descriptors has been reached")
+        elif exit_code == 126:
+            raise ProbablyPythonBug(
+                "a child process returned with exit status 126; this might "
+                "be the exit status of the dialog-like program, for some "
+                "unknown reason (-> probably a bug in the dialog-like "
+                "program); otherwise, we have probably found a python bug")
+        
+        # We might want to check here whether exit_code is really one of
+        # DIALOG_OK, DIALOG_CANCEL, etc. However, I prefer not doing it
+        # because it would break pythondialog for no strong reason when new
+        # exit codes are added to the dialog-like program.
+        #
+        # As it is now, if such a thing happens, the program using
+        # pythondialog may receive an exit_code it doesn't know about. OK, the
+        # programmer just has to tell the pythondialog maintainer about it and
+        # can temporarily set the appropriate DIALOG_* environment variable if
+        # he wants and assign the corresponding value to the Dialog instance's
+        # DIALOG_FOO attribute from his program. He doesn't even need to use a
+        # patched pythondialog before he upgrades to a version that knows
+        # about the new exit codes.
+        #
+        # The bad thing that might happen is a new DIALOG_FOO exit code being
+        # the same by default as one of those we chose for the other exit
+        # codes already known by pythondialog. But in this situation, the
+        # check that is being discussed wouldn't help at all.
+
+        # Read dialog's output on its stderr
+        try:
+            child_output = os.fdopen(child_rfd, "rb").read()
+            # Now, since the file object has no reference anymore, the
+            # standard IO stream behind it will be closed, causing the
+            # end of the the pipe we used to read dialog's output on its
+            # stderr to be closed (this is important, otherwise invoking
+            # dialog enough times will eventually exhaust the maximum number
+            # of open file descriptors).
+        except IOError, v:
+            raise PythonDialogIOError(v)
+
+        return (exit_code, child_output)
+
+    def _perform(self, cmdargs, **kwargs):
+	"""Perform a complete dialog-like program invocation.
+
+        This function invokes the dialog-like program, waits for its
+        termination and returns its exit status and whatever it wrote
+        on its standard error stream.
+
+        Notable exceptions:
+
+            any exception raised by self._call_program() or
+            self._wait_for_program_termination()
+
+        """
+        (child_pid, child_rfd) = \
+                    self._call_program(False, *(cmdargs,), **kwargs)
+        (exit_code, output) = \
+                    self._wait_for_program_termination(child_pid,
+                                                        child_rfd)
+	return (exit_code, output)
+
+    def _strip_xdialog_newline(self, output):
+        """Remove trailing newline (if any), if using Xdialog"""
+        if self.compat == "Xdialog" and output.endswith("\n"):
+            output = output[:-1]
+        return output
+
+    # This is for compatibility with the old dialog.py
+    def _perform_no_options(self, cmd):
+	"""Call dialog without passing any more options."""
+	return os.system(self._dialog_prg + ' ' + cmd)
+
+    # For compatibility with the old dialog.py
+    def clear(self):
+	"""Clear the screen. Equivalent to the dialog --clear option.
+
+        This method is obsolete. Please remove calls to it from your
+        programs.
+
+	"""
+	self._perform_no_options('--clear')
+
+    def calendar(self, text, height=6, width=0, day=0, month=0, year=0,
+                 **kwargs):
+        """Display a calendar dialog box.
+
+        text   -- text to display in the box
+        height -- height of the box (minus the calendar height)
+        width  -- width of the box
+        day    -- inititial day highlighted
+        month  -- inititial month displayed
+        year   -- inititial year selected (0 causes the current date
+                  to be used as the initial date)
+        
+        A calendar box displays month, day and year in separately
+        adjustable windows. If the values for day, month or year are
+        missing or negative, the current date's corresponding values
+        are used. You can increment or decrement any of those using
+        the left, up, right and down arrows. Use tab or backtab to
+        move between windows. If the year is given as zero, the
+        current date is used as an initial value.
+
+        Return a tuple of the form (code, date) where `code' is the
+        exit status (an integer) of the dialog-like program and
+        `date' is a list of the form [day, month, year] (where `day',
+        `month' and `year' are integers corresponding to the date
+        chosen by the user) if the box was closed with OK, or None if
+        it was closed with the Cancel button.
+
+        Notable exceptions:
+            - any exception raised by self._perform()
+            - UnexpectedDialogOutput
+            - PythonDialogReModuleError
+
+	"""
+	(code, output) = self._perform(
+            *(["--calendar", text, str(height), str(width), str(day),
+               str(month), str(year)],),
+            **kwargs)
+        if code == self.DIALOG_OK:
+            try:
+                mo = _calendar_date_rec.match(output)
+            except re.error, v:
+                raise PythonDialogReModuleError(v)
+            
+            if mo is None:
+                raise UnexpectedDialogOutput(
+                    "the dialog-like program returned the following "
+                    "unexpected date with the calendar box: %s" % output)
+            date = map(int, mo.group("day", "month", "year"))
+        else:
+            date = None
+        return (code, date)
+
+    def checklist(self, text, height=15, width=54, list_height=7,
+                  choices=[], **kwargs):
+	"""Display a checklist box.
+
+        text        -- text to display in the box
+        height      -- height of the box
+        width       -- width of the box
+        list_height -- number of entries displayed in the box (which
+                       can be scrolled) at a given time
+        choices     -- a list of tuples (tag, item, status) where
+                       `status' specifies the initial on/off state of
+                       each entry; can be 0 or 1 (integers, 1 meaning
+                       checked, i.e. "on"), or "on", "off" or any
+                       uppercase variant of these two strings.
+
+        Return a tuple of the form (code, [tag, ...]) with the tags
+        for the entries that were selected by the user. `code' is the
+        exit status of the dialog-like program.
+
+        If the user exits with ESC or CANCEL, the returned tag list
+        is empty.
+
+        Notable exceptions:
+
+            any exception raised by self._perform() or _to_onoff()
+
+        """
+        cmd = ["--checklist", text, str(height), str(width), str(list_height)]
+        for t in choices:
+            cmd.extend(((t[0], t[1], _to_onoff(t[2]))))
+
+        # The dialog output cannot be parsed reliably (at least in dialog
+        # 0.9b-20040301) without --separate-output (because double quotes in
+        # tags are escaped with backslashes, but backslashes are not
+        # themselves escaped and you have a problem when a tag ends with a
+        # backslash--the output makes you think you've encountered an embedded
+        # double-quote).
+        kwargs["separate_output"] = True
+
+	(code, output) = self._perform(*(cmd,), **kwargs)
+
+        # Since we used --separate-output, the tags are separated by a newline
+        # in the output. There is also a final newline after the last tag.
+        if output:
+            return (code, string.split(output, '\n')[:-1])
+        else:                           # empty selection
+            return (code, [])
+
+    def fselect(self, filepath, height, width, **kwargs):
+        """Display a file selection dialog box.
+
+        filepath -- initial file path
+        height   -- height of the box
+        width    -- width of the box
+        
+        The file-selection dialog displays a text-entry window in
+        which you can type a filename (or directory), and above that
+        two windows with directory names and filenames.
+
+        Here, filepath can be a file path in which case the file and
+        directory windows will display the contents of the path and
+        the text-entry window will contain the preselected filename.
+
+        Use tab or arrow keys to move between the windows. Within the
+        directory or filename windows, use the up/down arrow keys to
+        scroll the current selection. Use the space-bar to copy the
+        current selection into the text-entry window.
+
+        Typing any printable character switches focus to the
+        text-entry window, entering that character as well as
+        scrolling the directory and filename windows to the closest
+        match.
+
+        Use a carriage return or the "OK" button to accept the
+        current value in the text-entry window, or the "Cancel"
+        button to cancel.
+
+        Return a tuple of the form (code, path) where `code' is the
+        exit status (an integer) of the dialog-like program and
+        `path' is the path chosen by the user (whose last element may
+        be a directory or a file).
+              
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+        (code, output) = self._perform(
+            *(["--fselect", filepath, str(height), str(width)],),
+            **kwargs)
+
+        output = self._strip_xdialog_newline(output)
+        
+	return (code, output)
+    
+    def gauge_start(self, text="", height=8, width=54, percent=0, **kwargs):
+	"""Display gauge box.
+
+        text    -- text to display in the box
+        height  -- height of the box
+        width   -- width of the box
+        percent -- initial percentage shown in the meter
+
+        A gauge box displays a meter along the bottom of the box. The
+        meter indicates a percentage.
+
+        This function starts the dialog-like program telling it to
+        display a gauge box with a text in it and an initial
+        percentage in the meter.
+
+        Return value: undefined.
+
+
+        Gauge typical usage
+        -------------------
+
+        Gauge typical usage (assuming that `d' is an instance of the
+	Dialog class) looks like this:
+	    d.gauge_start()
+	    # do something
+	    d.gauge_update(10)       # 10% of the whole task is done
+	    # ...
+	    d.gauge_update(100, "any text here") # work is done
+	    exit_code = d.gauge_stop()           # cleanup actions
+
+
+        Notable exceptions:
+            - any exception raised by self._call_program()
+            - PythonDialogOSError
+
+	"""
+        (child_pid, child_rfd, child_stdin_wfd) = self._call_program(
+            True,
+            *(["--gauge", text, str(height), str(width), str(percent)],),
+            **kwargs)
+        try:
+            self._gauge_process = {
+                "pid": child_pid,
+                "stdin": os.fdopen(child_stdin_wfd, "wb"),
+                "child_rfd": child_rfd
+                }
+        except os.error, v:
+            raise PythonDialogOSError(v.strerror)
+            
+    def gauge_update(self, percent, text="", update_text=0):
+	"""Update a running gauge box.
+	
+        percent     -- new percentage to show in the gauge meter
+        text        -- new text to optionally display in the box
+        update-text -- boolean indicating whether to update the
+                       text in the box
+
+        This function updates the percentage shown by the meter of a
+        running gauge box (meaning `gauge_start' must have been
+        called previously). If update_text is true (for instance, 1),
+        the text displayed in the box is also updated.
+
+	See the `gauge_start' function's documentation for
+	information about how to use a gauge.
+
+        Return value: undefined.
+
+        Notable exception: PythonDialogIOError can be raised if there
+                           is an I/O error while writing to the pipe
+                           used to talk to the dialog-like program.
+
+	"""
+	if update_text:
+	    gauge_data = "%d\nXXX\n%s\nXXX\n" % (percent, text)
+	else:
+	    gauge_data = "%d\n" % percent
+	try:
+            self._gauge_process["stdin"].write(gauge_data)
+            self._gauge_process["stdin"].flush()
+        except IOError, v:
+            raise PythonDialogIOError(v)
+    
+    # For "compatibility" with the old dialog.py...
+    gauge_iterate = gauge_update
+
+    def gauge_stop(self):
+	"""Terminate a running gauge.
+
+        This function performs the appropriate cleanup actions to
+        terminate a running gauge (started with `gauge_start').
+	
+	See the `gauge_start' function's documentation for
+	information about how to use a gauge.
+
+        Return value: undefined.
+
+        Notable exceptions:
+            - any exception raised by
+              self._wait_for_program_termination()
+            - PythonDialogIOError can be raised if closing the pipe
+              used to talk to the dialog-like program fails.
+
+	"""
+        p = self._gauge_process
+        # Close the pipe that we are using to feed dialog's stdin
+        try:
+            p["stdin"].close()
+        except IOError, v:
+            raise PythonDialogIOError(v)
+        exit_code = \
+                  self._wait_for_program_termination(p["pid"],
+                                                      p["child_rfd"])[0]
+        return exit_code
+
+    def infobox(self, text, height=10, width=30, **kwargs):
+        """Display an information dialog box.
+
+        text   -- text to display in the box
+        height -- height of the box
+        width  -- width of the box
+
+        An info box is basically a message box. However, in this
+        case, dialog will exit immediately after displaying the
+        message to the user. The screen is not cleared when dialog
+        exits, so that the message will remain on the screen until
+        the calling shell script clears it later. This is useful
+        when you want to inform the user that some operations are
+        carrying on that may require some time to finish.
+
+        Return the exit status (an integer) of the dialog-like
+        program.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+	return self._perform(
+            *(["--infobox", text, str(height), str(width)],),
+            **kwargs)[0]
+
+    def inputbox(self, text, height=10, width=30, init='', **kwargs):
+        """Display an input dialog box.
+
+        text   -- text to display in the box
+        height -- height of the box
+        width  -- width of the box
+        init   -- default input string
+
+        An input box is useful when you want to ask questions that
+        require the user to input a string as the answer. If init is
+        supplied it is used to initialize the input string. When
+        entering the string, the BACKSPACE key can be used to
+        correct typing errors. If the input string is longer than
+        can fit in the dialog box, the input field will be scrolled.
+
+        Return a tuple of the form (code, string) where `code' is the
+        exit status of the dialog-like program and `string' is the
+        string entered by the user.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+        (code, tag) = self._perform(
+            *(["--inputbox", text, str(height), str(width), init],),
+            **kwargs)
+
+        tag = self._strip_xdialog_newline(tag)
+        
+	return (code, tag)
+
+    def menu(self, text, height=15, width=54, menu_height=7, choices=[],
+             **kwargs):
+        """Display a menu dialog box.
+
+        text        -- text to display in the box
+        height      -- height of the box
+        width       -- width of the box
+        menu_height -- number of entries displayed in the box (which
+                       can be scrolled) at a given time
+        choices     -- a sequence of (tag, item) or (tag, item, help)
+                       tuples (the meaning of each `tag', `item' and
+                       `help' is explained below)
+
+
+        Overview
+        --------
+
+        As its name suggests, a menu box is a dialog box that can be
+        used to present a list of choices in the form of a menu for
+        the user to choose. Choices are displayed in the order given.
+
+        Each menu entry consists of a `tag' string and an `item'
+        string. The tag gives the entry a name to distinguish it from
+        the other entries in the menu. The item is a short
+        description of the option that the entry represents.
+
+        The user can move between the menu entries by pressing the
+        UP/DOWN keys, the first letter of the tag as a hot-key, or
+        the number keys 1-9. There are menu-height entries displayed
+        in the menu at one time, but the menu will be scrolled if
+        there are more entries than that.
+
+
+        Providing on-line help facilities
+        ---------------------------------
+
+        If this function is called with item_help=1 (keyword
+        argument), the option --item-help is passed to dialog and the
+        tuples contained in `choices' must contain 3 elements each :
+        (tag, item, help). The help string for the highlighted item
+        is displayed in the bottom line of the screen and updated as
+        the user highlights other items.
+
+        If item_help=0 or if this keyword argument is not passed to
+        this function, the tuples contained in `choices' must contain
+        2 elements each : (tag, item).
+
+        If this function is called with help_button=1, it must also
+        be called with item_help=1 (this is a limitation of dialog),
+        therefore the tuples contained in `choices' must contain 3
+        elements each as explained in the previous paragraphs. This
+        will cause a Help button to be added to the right of the
+        Cancel button (by passing --help-button to dialog).
+
+
+        Return value
+        ------------
+
+        Return a tuple of the form (exit_info, string).
+
+        `exit_info' is either:
+          - an integer, being the the exit status of the dialog-like
+            program
+          - or the string "help", meaning that help_button=1 was
+            passed and that the user chose the Help button instead of
+            OK or Cancel.
+
+        The meaning of `string' depends on the value of exit_info:
+          - if `exit_info' is 0, `string' is the tag chosen by the
+            user
+          - if `exit_info' is "help", `string' is the `help' string
+            from the `choices' argument corresponding to the item
+            that was highlighted when the user chose the Help button
+          - otherwise (the user chose Cancel or pressed Esc, or there
+            was a dialog error), the value of `string' is undefined.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+        cmd = ["--menu", text, str(height), str(width), str(menu_height)]
+        for t in choices:
+            cmd.extend(t)
+	(code, output) = self._perform(*(cmd,), **kwargs)
+
+        output = self._strip_xdialog_newline(output)
+        
+        if "help_button" in kwargs.keys() and output.startswith("HELP "):
+            return ("help", output[5:])
+        else:
+            return (code, output)
+
+    def msgbox(self, text, height=10, width=30, **kwargs):
+        """Display a message dialog box.
+
+        text   -- text to display in the box
+        height -- height of the box
+        width  -- width of the box
+
+        A message box is very similar to a yes/no box. The only
+        difference between a message box and a yes/no box is that a
+        message box has only a single OK button. You can use this
+        dialog box to display any message you like. After reading
+        the message, the user can press the ENTER key so that dialog
+        will exit and the calling program can continue its
+        operation.
+
+        Return the exit status (an integer) of the dialog-like
+        program.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+	return self._perform(
+            *(["--msgbox", text, str(height), str(width)],),
+            **kwargs)[0]
+
+    def passwordbox(self, text, height=10, width=60, init='', **kwargs):
+        """Display an password input dialog box.
+
+        text   -- text to display in the box
+        height -- height of the box
+        width  -- width of the box
+        init   -- default input password
+
+        A password box is similar to an input box, except that the
+        text the user enters is not displayed. This is useful when
+        prompting for passwords or other sensitive information. Be
+        aware that if anything is passed in "init", it will be
+        visible in the system's process table to casual snoopers.
+        Also, it is very confusing to the user to provide them with a
+        default password they cannot see. For these reasons, using
+        "init" is highly discouraged.
+
+        Return a tuple of the form (code, password) where `code' is
+        the exit status of the dialog-like program and `password' is
+        the password entered by the user.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+	(code, password) = self._perform(
+            *(["--passwordbox", text, str(height), str(width), init],),
+            **kwargs)
+
+        password = self._strip_xdialog_newline(password)
+
+        return (code, password)
+
+    def radiolist(self, text, height=15, width=54, list_height=7,
+                  choices=[], **kwargs):
+	"""Display a radiolist box.
+
+        text        -- text to display in the box
+        height      -- height of the box
+        width       -- width of the box
+        list_height -- number of entries displayed in the box (which
+                       can be scrolled) at a given time
+        choices     -- a list of tuples (tag, item, status) where
+                       `status' specifies the initial on/off state
+                       each entry; can be 0 or 1 (integers, 1 meaning
+                       checked, i.e. "on"), or "on", "off" or any
+                       uppercase variant of these two strings.
+                       No more than one entry should  be set to on.
+
+        A radiolist box is similar to a menu box. The main difference
+        is that you can indicate which entry is initially selected,
+        by setting its status to on.
+
+        Return a tuple of the form (code, tag) with the tag for the
+        entry that was chosen by the user. `code' is the exit status
+        of the dialog-like program.
+
+        If the user exits with ESC or CANCEL, or if all entries were
+        initially set to off and not altered before the user chose
+        OK, the returned tag is the empty string.
+
+        Notable exceptions:
+
+            any exception raised by self._perform() or _to_onoff()
+
+	"""
+        cmd = ["--radiolist", text, str(height), str(width), str(list_height)]
+        for t in choices:
+            cmd.extend(((t[0], t[1], _to_onoff(t[2]))))
+
+        (code, tag) = self._perform(*(cmd,), **kwargs)
+
+        tag = self._strip_xdialog_newline(tag)
+            
+	return (code, tag)
+
+    def scrollbox(self, text, height=20, width=78, **kwargs):
+	"""Display a string in a scrollable box.
+
+        text   -- text to display in the box
+        height -- height of the box
+        width  -- width of the box
+
+        This method is a layer on top of textbox. The textbox option
+        in dialog allows to display file contents only. This method
+        allows you to display any text in a scrollable box. This is
+        simply done by creating a temporary file, calling textbox and
+        deleting the temporary file afterwards.
+
+        Return the dialog-like program's exit status.
+
+        Notable exceptions:
+            - UnableToCreateTemporaryDirectory
+            - PythonDialogIOError
+            - PythonDialogOSError
+            - exceptions raised by the tempfile module (which are
+              unfortunately not mentioned in its documentation, at
+              least in Python 2.3.3...)
+
+	"""
+        # In Python < 2.3, the standard library does not have
+        # tempfile.mkstemp(), and unfortunately, tempfile.mktemp() is
+        # insecure. So, I create a non-world-writable temporary directory and
+        # store the temporary file in this directory.
+        try:
+            # We want to ensure that f is already bound in the local
+            # scope when the finally clause (see below) is executed
+            f = 0
+            tmp_dir = _create_temporary_directory()
+            # If we are here, tmp_dir *is* created (no exception was raised),
+            # so chances are great that os.rmdir(tmp_dir) will succeed (as
+            # long as tmp_dir is empty).
+            #
+            # Don't move the _create_temporary_directory() call inside the
+            # following try statement, otherwise the user will always see a
+            # PythonDialogOSError instead of an
+            # UnableToCreateTemporaryDirectory because whenever
+            # UnableToCreateTemporaryDirectory is raised, the subsequent
+            # os.rmdir(tmp_dir) is bound to fail.
+            try:
+                fName = os.path.join(tmp_dir, "text")
+                # No race condition as with the deprecated tempfile.mktemp()
+                # since tmp_dir is not world-writable.
+                f = open(fName, "wb")
+                f.write(text)
+                f.close()
+
+                # Ask for an empty title unless otherwise specified
+                if not "title" in kwargs.keys():
+                    kwargs["title"] = ""
+
+                return self._perform(
+                    *(["--textbox", fName, str(height), str(width)],),
+                    **kwargs)[0]
+            finally:
+                if type(f) == types.FileType:
+                    f.close()           # Safe, even several times
+                    os.unlink(fName)
+                os.rmdir(tmp_dir)
+        except os.error, v:
+            raise PythonDialogOSError(v.strerror)
+        except IOError, v:
+            raise PythonDialogIOError(v)
+
+    def tailbox(self, filename, height=20, width=60, **kwargs):
+        """Display the contents of a file in a dialog box, as in "tail -f".
+
+        filename -- name of the file whose contents is to be
+                    displayed in the box
+        height   -- height of the box
+        width    -- width of the box
+
+        Display the contents of the specified file, updating the
+        dialog box whenever the file grows, as with the "tail -f"
+        command.
+
+        Return the exit status (an integer) of the dialog-like
+        program.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+	return self._perform(
+            *(["--tailbox", filename, str(height), str(width)],),
+            **kwargs)[0]
+    # No tailboxbg widget, at least for now.
+
+    def textbox(self, filename, height=20, width=60, **kwargs):
+        """Display the contents of a file in a dialog box.
+
+        filename -- name of the file whose contents is to be
+                    displayed in the box
+        height   -- height of the box
+        width    -- width of the box
+
+        A text box lets you display the contents of a text file in a
+        dialog box. It is like a simple text file viewer. The user
+        can move through the file by using the UP/DOWN, PGUP/PGDN
+        and HOME/END keys available on most keyboards. If the lines
+        are too long to be displayed in the box, the LEFT/RIGHT keys
+        can be used to scroll the text region horizontally. For more
+        convenience, forward and backward searching functions are
+        also provided.
+
+        Return the exit status (an integer) of the dialog-like
+        program.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+        # This is for backward compatibility... not that it is
+        # stupid, but I prefer explicit programming.
+        if not "title" in kwargs.keys():
+	    kwargs["title"] = filename
+	return self._perform(
+            *(["--textbox", filename, str(height), str(width)],),
+            **kwargs)[0]
+
+    def timebox(self, text, height=3, width=30, hour=-1, minute=-1,
+                second=-1, **kwargs):
+        """Display a time dialog box.
+
+        text   -- text to display in the box
+        height -- height of the box
+        width  -- width of the box
+        hour   -- inititial hour selected
+        minute -- inititial minute selected
+        second -- inititial second selected
+        
+        A dialog is displayed which allows you to select hour, minute
+        and second. If the values for hour, minute or second are
+        negative (or not explicitely provided, as they default to
+        -1), the current time's corresponding values are used. You
+        can increment or decrement any of those using the left-, up-,
+        right- and down-arrows. Use tab or backtab to move between
+        windows.
+
+        Return a tuple of the form (code, time) where `code' is the
+        exit status (an integer) of the dialog-like program and
+        `time' is a list of the form [hour, minute, second] (where
+        `hour', `minute' and `second' are integers corresponding to
+        the time chosen by the user) if the box was closed with OK,
+        or None if it was closed with the Cancel button.
+
+        Notable exceptions:
+            - any exception raised by self._perform()
+            - PythonDialogReModuleError
+            - UnexpectedDialogOutput
+
+	"""
+	(code, output) = self._perform(
+            *(["--timebox", text, str(height), str(width),
+               str(hour), str(minute), str(second)],),
+            **kwargs)
+        if code == self.DIALOG_OK:
+            try:
+                mo = _timebox_time_rec.match(output)
+                if mo is None:
+                    raise UnexpectedDialogOutput(
+                        "the dialog-like program returned the following "
+                        "unexpected time with the --timebox option: %s" % output)
+                time = map(int, mo.group("hour", "minute", "second"))
+            except re.error, v:
+                raise PythonDialogReModuleError(v)
+        else:
+            time = None
+        return (code, time)
+
+    def yesno(self, text, height=10, width=30, **kwargs):
+        """Display a yes/no dialog box.
+
+        text   -- text to display in the box
+        height -- height of the box
+        width  -- width of the box
+
+        A yes/no dialog box of size `height' rows by `width' columns
+        will be displayed. The string specified by `text' is
+        displayed inside the dialog box. If this string is too long
+        to fit in one line, it will be automatically divided into
+        multiple lines at appropriate places. The text string can
+        also contain the sub-string "\\n" or newline characters to
+        control line breaking explicitly. This dialog box is useful
+        for asking questions that require the user to answer either
+        yes or no. The dialog box has a Yes button and a No button,
+        in which the user can switch between by pressing the TAB
+        key.
+
+        Return the exit status (an integer) of the dialog-like
+        program.
+
+        Notable exceptions:
+
+            any exception raised by self._perform()
+
+	"""
+	return self._perform(
+            *(["--yesno", text, str(height), str(width)],),
+            **kwargs)[0]
Index: /branches/2.0/security/ExpressoCert/manifest.mf
===================================================================
--- /branches/2.0/security/ExpressoCert/manifest.mf	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/manifest.mf	(revision 1182)
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/setup/Setup.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/setup/Setup.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/setup/Setup.java	(revision 1182)
@@ -0,0 +1,182 @@
+package br.gov.serpro.setup;
+import java.applet.Applet;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import java.util.ResourceBundle;
+
+
+public class Setup {
+
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = -8164125429139606589L;
+
+	private Properties parameters;
+	private Properties preferences;
+    private Map<String, ResourceBundle> langResources;
+    Locale currentLocale;
+	private Applet currentApplet;
+	
+    private static final String PREFERENCES_PATH;
+    private static final String EPASS_2000;
+
+    static {
+
+        //PrintStream ps = new PrintStream(System.out);
+		//System.getProperties().list(System.out);
+
+        if (System.getProperty("os.name").equalsIgnoreCase("linux")){
+			EPASS_2000 = "/usr/lib/libepsng_p11.so";
+		}
+		else {
+			EPASS_2000 = System.getenv("SystemRoot").replaceAll("\\\\", "/")+"/system32/ngp11v211.dll";
+            //EPASS_2000 = System.getenv("ProgramFiles")+"\\Gemplus\\GemSafe Libraries\\BIN\\gclib.dll";
+		}
+
+
+		PREFERENCES_PATH = "TESTE";
+
+	}
+
+	public Setup(Applet applet){
+
+		this.currentApplet = applet;
+		parameters = new Properties();
+		//preferences = Do arquivo apontado por preferences_path
+
+		// Pega os parâmetros da applet
+		for (String[] parameter: getParameterInfo()){
+			String parameterName = parameter[0];
+			String parameterValue = this.currentApplet.getParameter(parameterName);
+
+			//System.out.println("Parameter a ser adicionado - " + parameterName + "= " + parameterValue);
+
+			if (parameterValue != null && !parameterValue.equals("")){
+				parameters.setProperty(parameterName.toLowerCase(), parameterValue);
+                if (parameterName.equalsIgnoreCase("locale")){
+                    System.out.println("Locale recebido.");
+                }
+			}
+            else {
+                //Defaults
+                if (parameterName.equalsIgnoreCase("debug")){
+                    parameters.setProperty(parameterName.toLowerCase(), "false");
+                }
+                if (parameterName.equalsIgnoreCase("token")){
+                    parameters.setProperty(parameterName.toLowerCase(), "Epass2000;"+EPASS_2000);
+                }
+                if (parameterName.equalsIgnoreCase("locale")){
+                    System.out.println("Locale não recebido, definindo valor default.");
+                    parameters.setProperty(parameterName.toLowerCase(), "pt_BR");
+                }
+            }
+		}
+
+		//TODO: Pega as preferências do arquivo de preferências se encontrado;
+
+        // Lang Resources
+        currentLocale = this.buildLocale(parameters.getProperty("locale"));
+        langResources = new HashMap<String, ResourceBundle>(2);
+        langResources.put("ExpressoCertMessages", ResourceBundle.getBundle("ExpressoCertMessages", currentLocale));
+
+	}
+
+	public String[][] getParameterInfo(){
+
+		String[][] info = {
+			{"debug", "boolean", "Habilita mensagens de debug"},
+            {"token", "string", "Lista de tokens suportados. Formato: nome1;caminho1,nome2;caminho2"},
+            {"locale", "string", "Locale do sistema"}
+		};
+
+		return info;
+	}
+
+	public String[][] getPreferencesInfo(){
+
+		String[][] info = {
+			{"preferedToken", "string", "Token preferencial do usuário. Formato: nome;caminho"}
+		};
+
+		return info;
+
+	}
+
+	public String getParameter(String key){
+		return parameters.getProperty(key);
+	}
+
+	public String getPreference(String key){
+		return getPreference(key);
+	}
+
+	//TODO: implementar PreferenceNotRegisteredException
+	public void setPreference(String key, String value){
+
+		boolean exists = false;
+		while (!exists){
+			for (String[] preference: getPreferencesInfo()){
+				if (key.equalsIgnoreCase(preference[1])){
+					exists = true;
+					preferences.setProperty(key, value);
+				}
+			}
+		}
+
+		if (!exists){
+//			 throws PreferenceNotRegisteredException();
+            System.out.println("Preferência não existe!");
+		}
+	}
+
+    Locale buildLocale(String localeCode){
+
+        String[] localeItems = localeCode.split("_");
+        Locale locale;
+        
+        switch (localeItems.length){
+            case 1: locale = new Locale(localeItems[0]);
+                    break;
+            case 2: locale = new Locale(localeItems[0], localeItems[1]);
+                    break;
+            case 3: locale = new Locale(localeItems[0], localeItems[1], localeItems[2]);
+                    break;
+            default: locale = new Locale("pt", "BR");
+                    System.out.println("Locale code error, setting default locale: " + locale.toString());
+        }
+
+        return locale;
+    }
+
+    public void addLanguageResource(String langResource){
+        System.out.println("registrando recusro de linguagem " + langResource);
+        langResources.put(langResource, ResourceBundle.getBundle(langResource, currentLocale));
+    }
+
+    public String getLang(String langResource, String message){
+
+        ResourceBundle resource = langResources.get(langResource);
+
+        String i18nText = new String("????");
+        try {
+            i18nText = resource.getString(message);
+        }
+        catch (MissingResourceException e){
+            e.printStackTrace();
+        }
+
+        return i18nText;
+
+        //return message;
+    }
+
+	// TODO: Not Implemented Yet
+	public boolean savePreferences(){
+		return false;
+	}
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/DigitalCertificate.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/DigitalCertificate.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/DigitalCertificate.java	(revision 1182)
@@ -0,0 +1,727 @@
+package br.gov.serpro.cert;
+
+import br.gov.serpro.setup.Setup;
+import java.awt.Frame;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AuthProvider;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.ProviderException;
+import java.security.Security;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.crypto.Cipher;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.net.ssl.SSLHandshakeException;
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.smime.SMIMECapability;
+import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
+import org.bouncycastle.mail.smime.SMIMEException;
+import org.bouncycastle.mail.smime.SMIMESignedGenerator;
+
+import br.gov.serpro.ui.DialogBuilder;
+import br.gov.serpro.util.Base64Utils;
+import com.sun.mail.util.BASE64DecoderStream;
+import java.security.AlgorithmParameters;
+import javax.mail.BodyPart;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.DERObject;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.RecipientId;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.mail.smime.SMIMEEnvelopedParser;
+import org.bouncycastle.mail.smime.SMIMEUtil;
+
+/**
+ * Classe que realiza todo o trabalho realizado com o certificado
+ * @author Mário César Kolling - mario.kolling@serpro.gov.br
+ */
+
+//TODO: Criar exceções para serem lançadas, entre elas DigitalCertificateNotLoaded
+//TODO: Adicionar setup
+
+public class DigitalCertificate {
+
+    private TokenCollection tokens;
+	private Certificate cert; // Certificado extraído da KeyStore. Pode ser nulo.
+	private KeyStore keyStore; // KeyStore que guarda o certificado do usuário. Pode ser nulo.
+
+	private Frame parentFrame;
+    private Setup setup;
+
+	// TODO: Transformar pkcs12Input em uma string ou URL com o caminho para a KeyStore pkcs12
+	private FileInputStream pkcs12Input; // stream da KeyStore pkcs12. Pode ser nulo.
+	private String providerName; // Nome do SecurityProvider pkcs11 carregado. Pode ser nulo.
+	private String hostAddress; // Endereço do host, onde a página principal do
+
+	private static final String HOME_SUBDIR; // Subdiretório dentro do diretório home do usuário. Dependente de SO.
+	private static final String EPASS_2000; // Caminho da biblioteca do token ePass2000. Dependente de SO.
+
+	private static final String CRLF = "\r\n"; // Separa campos na resposta do serviço de verificação de certificados
+	private static final String SUBJECT_ALTERNATIVE_NAME = "2.5.29.17"; // Não é mais utilizado.
+	private static final URL[] TRUST_STORES_URLS = new URL[3]; // URLs (file:/) das TrustStores, cacerts (jre),
+															   // trusted.certs e trusted.jssecerts (home do usuário)
+                                                               // Utilizadas para validação do certificado do servidor.
+	private static final String[] TRUST_STORES_PASSWORDS = null; // Senhas para cada uma das TrustStores,
+																 // caso seja necessário.
+	private int keystoreStatus;
+	public static final int KEYSTORE_DETECTED = 0;
+	public static final int KEYSTORE_NOT_DETECTED = 1;
+	public static final int KEYSTORE_ALREADY_LOADED = 2;
+
+	/*
+	 * Bloco estático que define os caminhos padrões da instalação da jre,
+	 * do diretório home do usuário, e da biblioteca de sistema do token ePass2000,
+	 * de acordo com o sistema operacional.
+	 */
+	static {
+
+		Properties systemProperties = System.getProperties();
+        Map<String, String> env = System.getenv();
+
+		/* TODO: Testar a existência de vários drivers de dispositivos. Determinar qual deve ser utilizado
+		 * e guardar em uma property no subdiretório home do usuário.
+		 */
+
+		if (systemProperties.getProperty("os.name").equalsIgnoreCase("linux")){
+			HOME_SUBDIR = "/.java/deployment/security";
+			EPASS_2000 = "/usr/lib/libepsng_p11.so";
+		}
+		else {
+			HOME_SUBDIR = "\\dados de aplicativos\\sun\\java\\deployment\\security";
+			EPASS_2000 = System.getenv("SystemRoot")+"\\system32\\ngp11v211.dll";
+            //EPASS_2000 = System.getenv("ProgramFiles")+"\\Gemplus\\GemSafe Libraries\\BIN\\gclib.dll";
+		}
+
+		try {
+			if (systemProperties.getProperty("os.name").equalsIgnoreCase("linux")){
+				TRUST_STORES_URLS[0] = new File(systemProperties.getProperty("java.home")
+					+ "/lib/security/cacerts").toURI().toURL();
+				TRUST_STORES_URLS[1] = new File(systemProperties.getProperty("user.home")
+					+ HOME_SUBDIR + "/trusted.certs").toURI().toURL();
+				TRUST_STORES_URLS[2] = new File(systemProperties.getProperty("user.home")
+					+ HOME_SUBDIR + "/trusted.jssecerts").toURI().toURL();
+			} else {
+
+				TRUST_STORES_URLS[0] = new File(systemProperties.getProperty("java.home") +
+						"\\lib\\security\\cacerts").toURI().toURL();
+				TRUST_STORES_URLS[1] = new File(systemProperties.getProperty("user.home") +
+						HOME_SUBDIR + "\\trusted.certs").toURI().toURL();
+				TRUST_STORES_URLS[2] = new File(systemProperties.getProperty("user.home") +
+						HOME_SUBDIR + "\\trusted.jssecerts").toURI().toURL();
+			}
+
+
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 *
+	 */
+	public DigitalCertificate() {
+		this.hostAddress = null;
+		this.parentFrame = null;
+	}
+
+	/**
+	 * Construtor da classe. Recebe a {@link URL} da página em que a Applet está incluída.
+	 * @param pageAddress URL da página em que a Applet está incluída
+	 */
+	private DigitalCertificate(URL pageAddress){
+		this.hostAddress = pageAddress.getHost();
+		this.parentFrame = null;
+	}
+
+	private DigitalCertificate(Frame parent){
+		this.hostAddress = null;
+		this.parentFrame = parent;
+	}
+
+
+    public DigitalCertificate(Frame parent, Setup setup){
+		this(parent);
+        this.setup = setup;
+	}
+
+    public DigitalCertificate(URL pageAddress, Setup setup){
+		this(pageAddress);
+        this.setup = setup;
+	}
+
+	/**
+	 * Destrói a Applet, removendo o security provider inicializado se o atributo providerName
+	 * for diferente de nulo.
+	 */
+	public void destroy(){
+
+		AuthProvider ap = null;
+
+        if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+            System.out.println("logout no provider");
+        }
+		if (keyStore != null){
+			ap = (AuthProvider) this.keyStore.getProvider();
+		}
+
+		if (ap != null){
+			try {
+				ap.logout();
+			} catch (LoginException e) {
+                if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                    e.printStackTrace();
+                }
+			}
+		}
+
+		if (providerName != null){
+			Security.removeProvider(providerName);
+		}
+
+		this.cert = null;
+		this.keyStore = null;
+		this.hostAddress = null;
+		this.pkcs12Input = null;
+		this.providerName = null;
+
+	}
+
+	/**
+	  * Procura pelo token nos locais padrões (Por enquanto só suporta o token ePass200),
+	  * senão procura por um certificado A1 em System.getProperties().getProperty("user.home") +
+	  * HOME_SUBDIR  + "/trusted.clientcerts" e retorna um inteiro de acordo com resultado desta procura.
+	  *
+	  * @author	Mário César Kolling
+	  * @return	Retorna um destes valores inteiros DigitalCertificate.KEYSTORE_DETECTED,
+	  * 		DigitalCertificate.KEYSTORE_ALREADY_LOADED ou DigitalCertificate.KEYSTORE_NOT_DETECTED
+	  * @see	DigitalCertificate
+	  */
+	public int init(){
+
+		// TODO: Usar dentro de um "loop" para testar outros modelos de tokens.
+        this.tokens = new TokenCollection(setup);
+		int interfaceType = DigitalCertificate.KEYSTORE_DETECTED;
+
+		try
+		{
+			// Tenta abrir o Token padrï¿œo (ePass2000).
+			loadKeyStore();
+
+		}
+		catch (Exception e1){
+
+			Provider[] providers = Security.getProviders();
+            if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                for (Provider provider: providers){
+                    System.out.println(provider.getInfo());
+                }
+
+                // Não conseguiu abrir o token (ePass2000).
+                System.out.println("Erro ao ler o token: " + e1.getMessage());
+            }
+
+			try
+			{
+				// Tenta abrir a keyStore padrão
+				// USER_HOME/deployment/security/trusted.clientcerts
+
+				Properties props = System.getProperties();
+				pkcs12Input = new FileInputStream(props.getProperty("user.home") +  HOME_SUBDIR  + "/trusted.clientcerts");
+
+				// Se chegar aqui significa que arquivo de KeyStore existe.
+				keyStore = KeyStore.getInstance("JKS");
+
+			}
+			catch (Exception ioe){
+				// Não conseguiu abrir a KeyStore pkcs12
+                if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                    System.out.println(ioe.getMessage());
+                }
+			}
+		}
+
+
+		if (keyStore == null){
+			// Não conseguiu inicializar a KeyStore. Mostra tela de login com usuário e senha.
+			this.keystoreStatus = DigitalCertificate.KEYSTORE_NOT_DETECTED;
+		//} else if (keyStore.getType().equalsIgnoreCase("pkcs11")){
+		} else {
+			// Usa certificado digital.
+			try {
+				// Testa se uma keystore já foi carregada previamente
+				if (keyStore.getType().equalsIgnoreCase("pkcs11")){
+					keyStore.load(null, null);
+				} else {
+					keyStore.load(pkcs12Input, null);
+				}
+
+				// Se chegou aqui KeyStore está liberada, mostrar tela de login sem pedir o pin.
+				this.keystoreStatus = DigitalCertificate.KEYSTORE_ALREADY_LOADED;
+
+			} catch (ProviderException e) {
+				// Algum erro ocorreu, mostra  tela de login com usuário e senha.
+				this.keystoreStatus = DigitalCertificate.KEYSTORE_NOT_DETECTED;
+				if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                    e.printStackTrace();
+                }
+			} catch (IOException e){
+				// KeyStore não está liberada, mostra tela de login com o pin.
+                if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                    System.out.println(e.getMessage());
+                }
+				this.keystoreStatus = DigitalCertificate.KEYSTORE_DETECTED;
+			} catch (GeneralSecurityException e){
+                if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                    e.printStackTrace();
+                }
+			}
+		}
+
+		return keystoreStatus;
+
+	}
+
+	/**
+	 * Usado para assinar digitalmente um e-mail.
+	 * @param mime
+	 * @return String vazia
+	 */
+	public String signMail (Map<String, String> data) throws IOException, GeneralSecurityException, SMIMEException, MessagingException{
+
+		Key privateKey = null;
+		if (this.keystoreStatus == DigitalCertificate.KEYSTORE_DETECTED){
+			String pin = DialogBuilder.showPinDialog(this.parentFrame, this.setup);
+			if (pin != null){
+				openKeyStore(pin.toCharArray());
+				privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), pin.toCharArray());
+			}
+			else {
+				return null;
+			}
+		}
+        /*
+		else if (this.keystoreStatus == DigitalCertificate.KEYSTORE_ALREADY_LOADED){
+				if (DialogBuilder.showPinNotNeededDialog(this.parentFrame)){
+					openKeyStore(null);
+					privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), null);
+				}
+				else {
+					return null;
+				}
+		}
+         */
+		else {
+
+            //DialogBuilder.showMessageDialog(this.parentFrame, "Nenhum token/smartcard foi detectado.\nOperação não pôde ser realizada!");
+            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate001"), this.setup);
+			return null;
+		}
+
+		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+		Certificate certificate = getCert();
+
+		KeyPair keypair = new KeyPair(certificate.getPublicKey(), (PrivateKey) privateKey);
+
+		// Cria a cadeia de certificados que a gente vai enviar
+        List certList = new ArrayList();
+
+        certList.add(certificate);
+
+        //
+        // create the base for our message
+		//
+        String fullMsg = data.get("body");
+
+        if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+            System.out.println("Corpo do e-mail:\n"+fullMsg+"\n");
+        }
+        
+        //
+        // Get a Session object and create the mail message
+        //
+        Properties props = System.getProperties();
+        Session session = Session.getDefaultInstance(props, null);
+
+        InputStream is = new ByteArrayInputStream(fullMsg.getBytes("iso-8859-1"));
+		MimeMessage unsignedMessage = new MimeMessage(session, is);
+
+        //
+        // create a CertStore containing the certificates we want carried
+        // in the signature
+        //
+        if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+            System.out.println("Provider: " + providerName);
+        }
+        CertStore certsAndcrls = CertStore.getInstance(
+                                "Collection",
+                                new CollectionCertStoreParameters(certList), "BC");
+
+        //
+        // create some smime capabilities in case someone wants to respond
+        //
+        ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
+
+        SMIMECapabilityVector       caps = new SMIMECapabilityVector();
+
+        caps.addCapability(SMIMECapability.dES_EDE3_CBC);
+        caps.addCapability(SMIMECapability.rC2_CBC, 128);
+        caps.addCapability(SMIMECapability.dES_CBC);
+
+		SMIMESignedGenerator gen = new SMIMESignedGenerator(unsignedMessage.getEncoding());
+
+		gen.addSigner(keypair.getPrivate(), (X509Certificate) certificate, SMIMESignedGenerator.DIGEST_SHA1, new AttributeTable(signedAttrs), null);
+
+		gen.addCertificatesAndCRLs(certsAndcrls);
+
+        //TODO: Extrair todos os headers de unsignedMessage
+
+		// Gera a assinatura
+		Object content = unsignedMessage.getContent();
+
+		//TODO: igualar unsignedMessage a null
+		//TODO: Pegar os headers do objeto que guardarï¿œ esses headers quando necessï¿œrio.
+
+		MimeMultipart mimeMultipartContent = null;
+		MimeBodyPart mimeBodyPartContent = new MimeBodyPart();
+
+		if (content.getClass().getName().contains("MimeMultipart")){
+			mimeMultipartContent = (MimeMultipart) content;
+		}
+		else {
+			is = new ByteArrayInputStream(content.toString().getBytes("iso-8859-1"));
+			mimeBodyPartContent = new MimeBodyPart(is);
+			mimeBodyPartContent.setHeader("Content-Type", unsignedMessage.getHeader("Content-Type", null)); //TODO: testar se valor existe
+			is = null;
+		}
+		content = null;
+
+		MimeBodyPart msg = null;
+		if (mimeMultipartContent == null){
+			msg = mimeBodyPartContent;
+			mimeBodyPartContent = null;
+		}
+		else {
+			msg = new MimeBodyPart();
+			msg.setContent(mimeMultipartContent);
+			mimeMultipartContent = null;
+		}
+
+        //
+        // extract the multipart object from the SMIMESigned object.
+        //
+        MimeMultipart mm = gen.generate(msg, providerName);
+        gen = null;
+        msg = null;
+
+        MimeMessage body = new MimeMessage(session);
+        body.setFrom(unsignedMessage.getFrom()[0]);
+        body.setRecipients(Message.RecipientType.TO, unsignedMessage.getRecipients(Message.RecipientType.TO));
+        body.setRecipients(Message.RecipientType.CC, unsignedMessage.getRecipients(Message.RecipientType.CC));
+        body.setRecipients(Message.RecipientType.BCC, unsignedMessage.getRecipients(Message.RecipientType.BCC));
+        body.setSubject(unsignedMessage.getSubject(), "iso-8859-1");
+
+        // Atrafuia o resto dos headers        
+        body.setHeader("Return-Path", unsignedMessage.getHeader("Return-Path", null));
+        body.setHeader("Message-ID", unsignedMessage.getHeader("Message-ID", null));
+        body.setHeader("X-Priority", unsignedMessage.getHeader("X-Priority", null));
+        body.setHeader("X-Mailer", unsignedMessage.getHeader("X-Mailer", null));
+        body.setHeader("Disposition-Notification-To", unsignedMessage.getHeader("Disposition-Notification-To", null));
+        body.setHeader("Date", unsignedMessage.getHeader("Date", null));
+        body.setContent(mm, mm.getContentType());
+        mm = null;
+
+        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+            System.out.println("\nHeaders do e-mail original:\n");
+        }
+
+        body.saveChanges();
+
+        ByteArrayOutputStream oStream = new ByteArrayOutputStream();
+        
+        oStream = new ByteArrayOutputStream();
+        body.writeTo(oStream);
+      
+        body = null;
+        return oStream.toString("iso-8859-1");
+
+	}
+    
+	/**
+	 * Método utilizado para criptografar um e-mail
+	 * @param source
+	 * @return
+	 */
+    public String cipherMail(Map<String, String> data) throws IOException, GeneralSecurityException, MessagingException, CMSException, SMIMEException{
+        
+        //Pega certificado do usuário.
+        
+        Key privateKey = null;
+		if (this.keystoreStatus == DigitalCertificate.KEYSTORE_DETECTED){
+			String pin = DialogBuilder.showPinDialog(this.parentFrame, this.setup);
+			if (pin != null){
+				openKeyStore(pin.toCharArray());
+				privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), pin.toCharArray());
+			}
+			else {
+				return null;
+			}
+		}
+        /*
+		else if (this.keystoreStatus == DigitalCertificate.KEYSTORE_ALREADY_LOADED){
+				if (DialogBuilder.showPinNotNeededDialog(this.parentFrame)){
+					openKeyStore(null);
+					privateKey = this.keyStore.getKey(keyStore.aliases().nextElement(), null);
+				}
+				else {
+					return null;
+				}
+		}
+         */
+		else {
+
+            //DialogBuilder.showMessageDialog(this.parentFrame, "Nenhum token/smartcard foi detectado.\nOperação não pôde ser realizada!");
+            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate001"), this.setup);
+			return null;
+		}
+
+		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+		X509Certificate cert = (X509Certificate)getCert();
+        
+        RecipientId recId = new RecipientId();
+        recId.setSerialNumber(cert.getSerialNumber());
+        recId.setIssuer(cert.getIssuerX500Principal());
+        
+        Properties props = System.getProperties();
+        Session session = Session.getDefaultInstance(props, null);
+        
+        String fullMsg = data.get("body");
+        InputStream is = new ByteArrayInputStream(fullMsg.getBytes("iso-8859-1"));
+        MimeMessage encriptedMsg = new MimeMessage(session, is);
+
+        Provider prov = Security.getProvider(providerName);
+        if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+            System.out.println("Serviços do provider "+providerName+ ":\n"+ prov.getInfo());
+            for (Provider.Service service: prov.getServices())
+                System.out.println(service.toString() + ": " + service.getAlgorithm());
+        }
+
+        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+            System.out.println("Email criptografado:\n" + fullMsg);
+        }
+
+        SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(encriptedMsg);
+        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+            System.out.println("Algoritmo de encriptação: " + m.getEncryptionAlgOID());
+        }
+
+        AlgorithmParameters algParams = m.getEncryptionAlgorithmParameters("BC");
+        if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+            System.out.println("Parâmetros do algoritmo: " + algParams.toString());
+        }
+
+        RecipientInformationStore recipients = m.getRecipientInfos();
+        RecipientInformation recipient = recipients.get(recId);
+
+        if (recipient != null){
+            MimeBodyPart decriptedBodyPart = SMIMEUtil.toMimeBodyPart(recipient.getContentStream(privateKey, this.providerName));
+            encriptedMsg.setContent(decriptedBodyPart.getContent(), decriptedBodyPart.getContentType());
+            encriptedMsg.saveChanges();
+
+            ByteArrayOutputStream oStream = new ByteArrayOutputStream();
+            encriptedMsg.writeTo(oStream);
+            encriptedMsg = null;
+
+            return oStream.toString("iso-8859-1");
+        }
+        else {
+            //DialogBuilder.showMessageDialog(this.parentFrame, "Não é possível ler este e-mail com o Certificado Digital apresentado!\n" +
+            //        "Motivo: Este e-mail não foi cifrado com a chave pública deste Certificado Digital.");
+            DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMessages", "DigitalCertificate002"), this.setup);
+            return null;
+        }
+    }
+
+	/**
+	 * Pega as credenciais de login do dono do certificado do serviço de verificação de certificados
+	 * @param	pin			pin para acessar o token
+	 * @return	resposta	Array de Strings em que:
+	 * 						Indice 0: código de retorno;
+	 * 						Indice 1: username se código de retorno for 0, senão mensagem de erro;
+	 * 						Indice 2: senha decriptada se código de retorno for 0, senão não existe;
+	 * @throws SSLHandshakeException
+	 * @throws HttpException
+	 * @throws IOException
+	 * @throws GeneralSecurityException
+	 */
+	public String[] getCredentials(String pin) throws SSLHandshakeException, HttpException, IOException, GeneralSecurityException{
+
+		String[] resposta = null;
+
+        if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+            System.out.println("Proxy Configurado no browser: " + System.getProperty("http.proxyHost")
+                    + ":" + System.getProperty("http.proxyPort"));
+        }
+
+		// Registra novo protocolo https, utilizando nova implementação de AuthSSLProtocolSocketFactory
+		Protocol.registerProtocol("https", new Protocol("https",
+				(ProtocolSocketFactory) new AuthSSLProtocolSocketFactory(TRUST_STORES_URLS, TRUST_STORES_PASSWORDS, this.setup),
+				443));
+
+		HttpClient httpclient = new HttpClient();
+		// Define um método post para o link do serviço de verificação de certificados
+        if (System.getProperty("http.proxyHost") != null && System.getProperty("http.proxyPort") != null){
+            httpclient.getHostConfiguration().setProxy(System.getProperty("http.proxyHost"),
+                    Integer.parseInt(System.getProperty("http.proxyPort")));
+        }
+		PostMethod httppost = new PostMethod("https://"+ hostAddress + "/security/vercert.php");
+
+		try {
+			// Adiciona parâmetro certificado no método post, executa o método, pega a resposta do servidor
+			// como uma string com CRLF de separador entre os campos e gera um array de Strings
+			httppost.addParameter("certificado", Base64Utils.der2pem(cert.getEncoded()));
+			httpclient.executeMethod(httppost);
+			resposta = httppost.getResponseBodyAsString().split(CRLF);
+
+			if (resposta.length > 2){
+				if (Integer.parseInt(resposta[0].trim()) == 0){
+					// Se código da resposta for zero, decripta a senha criptografada do usuário
+					resposta[2] = decriptPassword(resposta[2].trim(), pin);
+				}
+			}
+
+		} catch (IOException e){
+			// Se for instância de SSLHandshakeException faz um cast para este tipo e lança a exceção novamente
+			// Isto é usado para diferenciar o tipo de falha, para que a mensagem para o usuário seja mostrada de
+			// acordo.
+			if (e instanceof SSLHandshakeException){
+				throw (SSLHandshakeException) e;
+			}
+			// senão lança novamente a exceção do tipo IOException
+			throw e;
+		} finally {
+			// fecha a conexão
+			httppost.releaseConnection();
+		}
+
+		return resposta;
+	}
+
+	/**
+	 * Decripta a senha criptografada
+	 * @param encodedPassword senha criptografada e codificada em base64 para ser decriptada
+	 * @param pin pin para acessar a KeyStore
+	 * @return decodedPassword
+	 * @throws GeneralSecurityException se algum problema ocorrer na decriptação da senha.
+	 */
+	public String decriptPassword(String encodedPassword, String pin) throws GeneralSecurityException{
+
+		String decodedPassword = new String();
+
+		// Pega a chave privada do primeiro certificado armazenado na KeyStore
+		Enumeration<String> en = this.keyStore.aliases();
+		String certAlias = en.nextElement();
+		Key privateKey = this.keyStore.getKey(certAlias, pin.toCharArray());
+
+		// Inicializa os cipher com os parâmetros corretos para realizar a decriptação
+		Cipher dcipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+		dcipher.init(Cipher.DECRYPT_MODE, privateKey);
+
+		// Decodifica a senha em base64 e a decripta
+		decodedPassword = new String(dcipher.doFinal(Base64Utils.base64Decode(encodedPassword)));
+
+		return decodedPassword.trim();
+
+	}
+
+	/**
+	 * Carrega um novo SecurityProvider
+	 * @param pkcs11Config Linha de configuração do SmartCard ou Token
+	 * @throws KeyStoreException Quando não conseguir iniciar a KeyStore, ou a lib do Token
+	 * 							 ou Smartcard não foi encontrada, ou o usuário não inseriu o Token.
+	 */
+	private void loadKeyStore() throws KeyStoreException{
+
+		//Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(new ByteArrayInputStream(pkcs11Config.getBytes()));
+		//Security.addProvider(pkcs11Provider);
+		this.keyStore = KeyStore.getInstance("PKCS11");
+		this.providerName = keyStore.getProvider().getName();
+
+	}
+
+	/**
+	 *  Abre a keystore passando o pin
+	 *  @param pin pin para acessar o Token
+	 */
+	public void openKeyStore(char[] pin) throws IOException{
+
+		try {
+
+			if (this.keyStore.getType().equals("PKCS11")){
+				this.keyStore.load(null, pin);
+			}
+			else {
+				this.keyStore.load(this.pkcs12Input, pin);
+			}
+
+			this.cert = this.keyStore.getCertificate(this.keyStore.aliases().nextElement());
+
+		} catch (GeneralSecurityException e) {
+            if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+                e.printStackTrace();
+            }
+		}
+
+	}
+
+	/**
+	 * @return the cert
+	 */
+	Certificate getCert() {
+		return this.cert;
+	}
+
+	/**
+	 * @param cert the cert to set
+	 */
+	void setCert(Certificate cert) {
+		this.cert = cert;
+	}
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/Token.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/Token.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/Token.java	(revision 1182)
@@ -0,0 +1,104 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package br.gov.serpro.cert;
+
+import br.gov.serpro.setup.Setup;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.security.Provider;
+import java.security.ProviderException;
+import java.security.Security;
+
+//TODO: Deal with wildcards for environments variables.
+
+/**
+ *
+ * @author esa
+ */
+class Token{
+
+    private final Setup setup;
+    private String name;
+    private String libraryPath;
+    private Provider tokenProvider;
+    private boolean registered = false;
+
+    private Token(final Setup setup) {
+        this.setup = setup;
+    }
+
+    Token(String name, String libraryPath, final Setup setup){
+        this(setup);
+        this.setName(name);
+        this.setLibraryPath(libraryPath);
+    }
+
+    public boolean isRegistered() {
+        return this.registered;
+    }
+
+    public void setLibraryPath(String libraryPath) {
+        this.libraryPath = libraryPath;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    protected void registerToken(){
+        String tokenConfiguration = new String("name = " + name + "\n" +
+            "library = " + libraryPath + "\ndisabledMechanisms = {\n" +
+            "CKM_SHA1_RSA_PKCS\n}");
+
+        try{
+            this.registered = false;
+            if (libraryExists()){
+                Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(new ByteArrayInputStream(tokenConfiguration.getBytes()));
+                this.tokenProvider = pkcs11Provider;
+                Security.addProvider(pkcs11Provider);
+                this.registered = true;
+            }
+        }
+        catch (ProviderException e){
+            if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                e.printStackTrace();
+                System.out.println("Não foi possível inicializar o seguinte token: " + tokenConfiguration);
+            }
+        }
+    }
+
+    protected void unregisterToken(){
+        Security.removeProvider(this.tokenProvider.getName());
+    }
+
+/*    public KeyStore getKeystore() throws KeyStoreException {
+
+        return (this.keyStore = KeyStore.getInstance("PKCS11"));
+
+    }
+*/
+    public boolean libraryExists(){
+
+        File libraryFile = new File(libraryPath);
+        if (libraryFile.exists()){
+            if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                System.out.println("Arquivo " + libraryPath + " existe.");
+            }
+            return true;
+        }
+        
+        if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+            System.out.println("Biblioteca do Token/SmartCard " + name + " não foi encontrada: " + libraryPath);
+        }
+        
+        return false;
+    }
+    
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLInitializationError.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLInitializationError.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLInitializationError.java	(revision 1182)
@@ -0,0 +1,70 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package br.gov.serpro.cert;
+
+/**
+ * <p>
+ * Signals fatal error in initialization of {@link AuthSSLProtocolSocketFactory}.
+ * </p>
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *
+ * <p>
+ * DISCLAIMER: HttpClient developers DO NOT actively support this component.
+ * The component is provided as a reference material, which may be inappropriate
+ * for use without additional customization.
+ * </p>
+ */
+
+
+public class AuthSSLInitializationError extends Error {
+
+    /**
+	 *
+	 */
+	private static final long serialVersionUID = 2239180746163317934L;
+
+	/**
+     * Creates a new AuthSSLInitializationError.
+     */
+    public AuthSSLInitializationError() {
+        super();
+    }
+
+    /**
+     * Creates a new AuthSSLInitializationError with the specified message.
+     *
+     * @param message error message
+     */
+    public AuthSSLInitializationError(String message) {
+        super(message);
+    }
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLProtocolSocketFactory.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLProtocolSocketFactory.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLProtocolSocketFactory.java	(revision 1182)
@@ -0,0 +1,460 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package br.gov.serpro.cert;
+
+import br.gov.serpro.setup.Setup;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * <p>
+ * AuthSSLProtocolSocketFactory can be used to validate the identity of the HTTPS
+ * server against a list of trusted certificates and to authenticate to the HTTPS
+ * server using a private key.
+ * </p>
+ *
+ * <p>
+ * AuthSSLProtocolSocketFactory will enable server authentication when supplied with
+ * a {@link KeyStore truststore} file containg one or several trusted certificates.
+ * The client secure socket will reject the connection during the SSL session handshake
+ * if the target HTTPS server attempts to authenticate itself with a non-trusted
+ * certificate.
+ * </p>
+ *
+ * <p>
+ * Use JDK keytool utility to import a trusted certificate and generate a truststore file:
+ *    <pre>
+ *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
+ *    </pre>
+ * </p>
+ *
+ * <p>
+ * AuthSSLProtocolSocketFactory will enable client authentication when supplied with
+ * a {@link KeyStore keystore} file containg a private key/public certificate pair.
+ * The client secure socket will use the private key to authenticate itself to the target
+ * HTTPS server during the SSL session handshake if requested to do so by the server.
+ * The target HTTPS server will in its turn verify the certificate presented by the client
+ * in order to establish client's authenticity
+ * </p>
+ *
+ * <p>
+ * Use the following sequence of actions to generate a keystore file
+ * </p>
+ *   <ul>
+ *     <li>
+ *      <p>
+ *      Use JDK keytool utility to generate a new key
+ *      <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
+ *      For simplicity use the same password for the key as that of the keystore
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *      Issue a certificate signing request (CSR)
+ *      <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
+ *     </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *      Send the certificate request to the trusted Certificate Authority for signature.
+ *      One may choose to act as her own CA and sign the certificate request using a PKI
+ *      tool, such as OpenSSL.
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Import the trusted CA root certificate
+ *       <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre>
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Import the PKCS#7 file containg the complete certificate chain
+ *       <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre>
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Verify the content the resultant keystore file
+ *       <pre>keytool -list -v -keystore my.keystore</pre>
+ *      </p>
+ *     </li>
+ *   </ul>
+ * <p>
+ * Example of using custom protocol socket factory for a specific host:
+ *     <pre>
+ *     Protocol authhttps = new Protocol("https",
+ *          new AuthSSLProtocolSocketFactory(
+ *              new URL("file:my.keystore"), "mypassword",
+ *              new URL("file:my.truststore"), "mypassword"), 443);
+ *
+ *     HttpClient client = new HttpClient();
+ *     client.getHostConfiguration().setHost("localhost", 443, authhttps);
+ *     // use relative url only
+ *     GetMethod httpget = new GetMethod("/");
+ *     client.executeMethod(httpget);
+ *     </pre>
+ * </p>
+ * <p>
+ * Example of using custom protocol socket factory per default instead of the standard one:
+ *     <pre>
+ *     Protocol authhttps = new Protocol("https",
+ *          new AuthSSLProtocolSocketFactory(
+ *              new URL("file:my.keystore"), "mypassword",
+ *              new URL("file:my.truststore"), "mypassword"), 443);
+ *     Protocol.registerProtocol("https", authhttps);
+ *
+ *     HttpClient client = new HttpClient();
+ *     GetMethod httpget = new GetMethod("https://localhost/");
+ *     client.executeMethod(httpget);
+ *     </pre>
+ * </p>
+ * @author <a href="mailto:oleg -at- ural.ru">Oleg Kalnichevski</a>
+ *
+ * <p>
+ * DISCLAIMER: HttpClient developers DO NOT actively support this component.
+ * The component is provided as a reference material, which may be inappropriate
+ * for use without additional customization.
+ * </p>
+ */
+
+public class AuthSSLProtocolSocketFactory implements
+		SecureProtocolSocketFactory {
+
+    /** Log object for this class. */
+    private static final Log LOG = LogFactory.getLog(AuthSSLProtocolSocketFactory.class);
+
+    private URL[] truststoreUrls = null;
+    private String[] truststorePasswords = null;
+    private SSLContext sslcontext = null;
+    private static Setup setup = null;
+
+    /**
+     * Constructor for AuthSSLProtocolSocketFactory. Either a keystore or truststore file
+     * must be given. Otherwise SSL context initialization error will result.
+     *
+     * @param keystoreUrl URL of the keystore file. May be <tt>null</tt> if HTTPS client
+     *        authentication is not to be used.
+     * @param keystorePassword Password to unlock the keystore. IMPORTANT: this implementation
+     *        assumes that the same password is used to protect the key and the keystore itself.
+     * @param truststoreUrls URL of the truststore file. May be <tt>null</tt> if HTTPS server
+     *        authentication is not to be used.
+     * @param truststorePasswords Password to unlock the truststore.
+     */
+    public AuthSSLProtocolSocketFactory(
+        final URL[] truststoreUrls, final String[] truststorePasswords, final Setup setup)
+    {
+        super();
+        this.truststoreUrls = truststoreUrls;
+        this.truststorePasswords = truststorePasswords;
+        AuthSSLProtocolSocketFactory.setup = setup;
+    }
+
+    // TODO: Usar um mapa de keystores e senhas ou algo como KeystoreCredentialsPair
+    private static KeyStore createKeyStore(final URL[] urls, final String[] passwords)
+        throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException
+    {
+    	KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+    	keystore.load(null);
+
+        if (urls == null) {
+            throw new IllegalArgumentException("Keystore urls may not be null");
+        }
+
+        if (passwords != null && passwords.length != urls.length){
+        	throw new IllegalArgumentException("Urls and passwords arrays must have the same size");
+        }
+
+        LOG.debug("Initializing key store");
+
+        for (int i = 0; i < urls.length; i++){
+
+        	LOG.debug("Adding " + urls[i].toString() + " to internal keystore");
+	        KeyStore ks  = KeyStore.getInstance("jks");
+	        InputStream is = null;
+	        try {
+	        	is = urls[i].openStream();
+
+	        	if (passwords == null){
+	        		ks.load(is, null);
+	        	}
+	        	else {
+	        		ks.load(is, passwords[i] != null ? passwords[i].toCharArray(): null);
+	        	}
+
+	            for (Enumeration<String> e = ks.aliases(); e.hasMoreElements();){
+	            	X509Certificate cert = (X509Certificate) ks.getCertificate(e.nextElement());
+	            	keystore.setCertificateEntry(cert.getSubjectX500Principal().getName(), cert);
+	            }
+	        } catch (IOException e){
+                if (AuthSSLProtocolSocketFactory.setup.getParameter("debug").equalsIgnoreCase("true")){
+                    System.out.println("Erro ao abrir URL: " + urls[i].toExternalForm());
+                }
+        	} finally {
+	        	if (is != null) is.close();
+	        }
+        }
+        return keystore;
+    }
+
+    private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
+        throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException
+    {
+        if (keystore == null) {
+            throw new IllegalArgumentException("Keystore may not be null");
+        }
+        LOG.debug("Initializing key manager");
+        KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
+            KeyManagerFactory.getDefaultAlgorithm());
+        kmfactory.init(keystore, password != null ? password.toCharArray(): null);
+        return kmfactory.getKeyManagers();
+    }
+
+    private static TrustManager[] createTrustManagers(final KeyStore keystore)
+        throws KeyStoreException, NoSuchAlgorithmException
+    {
+        if (keystore == null) {
+            throw new IllegalArgumentException("Keystore may not be null");
+        }
+        LOG.debug("Initializing trust manager");
+        TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
+            TrustManagerFactory.getDefaultAlgorithm());
+        tmfactory.init(keystore);
+        TrustManager[] trustmanagers = tmfactory.getTrustManagers();
+        for (int i = 0; i < trustmanagers.length; i++) {
+            if (trustmanagers[i] instanceof X509TrustManager) {
+                trustmanagers[i] = new AuthSSLX509TrustManager(
+                    (X509TrustManager)trustmanagers[i]);
+            }
+        }
+        return trustmanagers;
+    }
+
+    private SSLContext createSSLContext() {
+        try {
+           // KeyManager[] keymanagers = null;
+            TrustManager[] trustmanagers = null;
+            /*
+            if (this.keystoreUrl != null) {
+                KeyStore keystore = createKeyStore(this.keystoreUrl, this.keystorePassword);
+                if (LOG.isDebugEnabled()) {
+                    Enumeration aliases = keystore.aliases();
+                    while (aliases.hasMoreElements()) {
+                        String alias = (String)aliases.nextElement();
+                        Certificate[] certs = keystore.getCertificateChain(alias);
+                        if (certs != null) {
+                            LOG.debug("Certificate chain '" + alias + "':");
+                            for (int c = 0; c < certs.length; c++) {
+                                if (certs[c] instanceof X509Certificate) {
+                                    X509Certificate cert = (X509Certificate)certs[c];
+                                    LOG.debug(" Certificate " + (c + 1) + ":");
+                                    LOG.debug("  Subject DN: " + cert.getSubjectDN());
+                                    LOG.debug("  Signature Algorithm: " + cert.getSigAlgName());
+                                    LOG.debug("  Valid from: " + cert.getNotBefore() );
+                                    LOG.debug("  Valid until: " + cert.getNotAfter());
+                                    LOG.debug("  Issuer: " + cert.getIssuerDN());
+                                }
+                            }
+                        }
+                    }
+                }
+                keymanagers = createKeyManagers(keystore, this.keystorePassword);
+            }
+            */
+            if (this.truststoreUrls != null) {
+                KeyStore keystore = createKeyStore(this.truststoreUrls, this.truststorePasswords);
+                if (LOG.isDebugEnabled()) {
+                    Enumeration aliases = keystore.aliases();
+                    while (aliases.hasMoreElements()) {
+                        String alias = (String)aliases.nextElement();
+                        LOG.debug("Trusted certificate '" + alias + "':");
+                        Certificate trustedcert = keystore.getCertificate(alias);
+                        if (trustedcert != null && trustedcert instanceof X509Certificate) {
+                            X509Certificate cert = (X509Certificate)trustedcert;
+                            LOG.debug("  Subject DN: " + cert.getSubjectDN());
+                            LOG.debug("  Signature Algorithm: " + cert.getSigAlgName());
+                            LOG.debug("  Valid from: " + cert.getNotBefore() );
+                            LOG.debug("  Valid until: " + cert.getNotAfter());
+                            LOG.debug("  Issuer: " + cert.getIssuerDN());
+                        }
+                    }
+                }
+                trustmanagers = createTrustManagers(keystore);
+            }
+            SSLContext sslcontext = SSLContext.getInstance("SSL");
+            sslcontext.init(null, trustmanagers, null);
+            return sslcontext;
+        } catch (NoSuchAlgorithmException e) {
+            LOG.error(e.getMessage(), e);
+            throw new AuthSSLInitializationError("Unsupported algorithm exception: " + e.getMessage());
+        } catch (KeyStoreException e) {
+            LOG.error(e.getMessage(), e);
+            throw new AuthSSLInitializationError("Keystore exception: " + e.getMessage());
+        } catch (GeneralSecurityException e) {
+            LOG.error(e.getMessage(), e);
+            throw new AuthSSLInitializationError("Key management exception: " + e.getMessage());
+        } catch (IOException e) {
+            LOG.error(e.getMessage(), e);
+            throw new AuthSSLInitializationError("I/O error reading keystore/truststore file: " + e.getMessage());
+        }
+    }
+
+    private SSLContext getSSLContext() {
+        if (this.sslcontext == null) {
+            this.sslcontext = createSSLContext();
+        }
+        return this.sslcontext;
+    }
+
+    /**
+     * Attempts to get a new socket connection to the given host within the given time limit.
+     * <p>
+     * To circumvent the limitations of older JREs that do not support connect timeout a
+     * controller thread is executed. The controller thread attempts to create a new socket
+     * within the given limit of time. If socket constructor does not return until the
+     * timeout expires, the controller terminates and throws an {@link ConnectTimeoutException}
+     * </p>
+     *
+     * @param host the host name/IP
+     * @param port the port on the host
+     * @param clientHost the local host name/IP to bind the socket to
+     * @param clientPort the port on the local machine
+     * @param params {@link HttpConnectionParams Http connection parameters}
+     *
+     * @return Socket a new socket
+     *
+     * @throws IOException if an I/O error occurs while creating the socket
+     * @throws UnknownHostException if the IP address of the host cannot be
+     * determined
+     */
+    public Socket createSocket(
+        final String host,
+        final int port,
+        final InetAddress localAddress,
+        final int localPort,
+        final HttpConnectionParams params
+    ) throws IOException, UnknownHostException, ConnectTimeoutException {
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters may not be null");
+        }
+        int timeout = params.getConnectionTimeout();
+        SocketFactory socketfactory = getSSLContext().getSocketFactory();
+        if (timeout == 0) {
+            return socketfactory.createSocket(host, port, localAddress, localPort);
+        } else {
+            Socket socket = socketfactory.createSocket();
+            SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
+            SocketAddress remoteaddr = new InetSocketAddress(host, port);
+            socket.bind(localaddr);
+            socket.connect(remoteaddr, timeout);
+            return socket;
+        }
+    }
+
+    /**
+     * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
+     */
+    public Socket createSocket(
+        String host,
+        int port,
+        InetAddress clientHost,
+        int clientPort)
+        throws IOException, UnknownHostException
+   {
+       return getSSLContext().getSocketFactory().createSocket(
+            host,
+            port,
+            clientHost,
+            clientPort
+        );
+    }
+
+    /**
+     * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
+     */
+    public Socket createSocket(String host, int port)
+        throws IOException, UnknownHostException
+    {
+        return getSSLContext().getSocketFactory().createSocket(
+            host,
+            port
+        );
+    }
+
+    /**
+     * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
+     */
+    public Socket createSocket(
+        Socket socket,
+        String host,
+        int port,
+        boolean autoClose)
+        throws IOException, UnknownHostException
+    {
+        return getSSLContext().getSocketFactory().createSocket(
+            socket,
+            host,
+            port,
+            autoClose
+        );
+    }
+
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLX509TrustManager.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLX509TrustManager.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/AuthSSLX509TrustManager.java	(revision 1182)
@@ -0,0 +1,142 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package br.gov.serpro.cert;
+
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509TrustManager;
+import javax.swing.JOptionPane;
+
+import java.security.cert.CertificateException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * <p>
+ * AuthSSLX509TrustManager can be used to extend the default {@link X509TrustManager}
+ * with additional trust decisions.
+ * </p>
+ *
+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
+ *
+ * <p>
+ * DISCLAIMER: HttpClient developers DO NOT actively support this component.
+ * The component is provided as a reference material, which may be inappropriate
+ * for use without additional customization.
+ * </p>
+ */
+
+public class AuthSSLX509TrustManager implements X509TrustManager {
+
+    private X509TrustManager defaultTrustManager = null;
+
+    /** Log object for this class. */
+    private static final Log LOG = LogFactory.getLog(AuthSSLX509TrustManager.class);
+
+    /**
+     * Constructor for AuthSSLX509TrustManager.
+     */
+    public AuthSSLX509TrustManager(final X509TrustManager defaultTrustManager) {
+        super();
+        if (defaultTrustManager == null) {
+            throw new IllegalArgumentException("Trust manager may not be null");
+        }
+        this.defaultTrustManager = defaultTrustManager;
+    }
+
+    /**
+     * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)
+     */
+    public void checkClientTrusted(X509Certificate[] certificates,String authType) throws CertificateException {
+        if (LOG.isInfoEnabled() && certificates != null) {
+            for (int c = 0; c < certificates.length; c++) {
+                X509Certificate cert = certificates[c];
+                LOG.info(" Client certificate " + (c + 1) + ":");
+                LOG.info("  Subject DN: " + cert.getSubjectDN());
+                LOG.info("  Signature Algorithm: " + cert.getSigAlgName());
+                LOG.info("  Valid from: " + cert.getNotBefore() );
+                LOG.info("  Valid until: " + cert.getNotAfter());
+                LOG.info("  Issuer: " + cert.getIssuerDN());
+            }
+        }
+        defaultTrustManager.checkClientTrusted(certificates,authType);
+    }
+
+    /**
+     * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)
+     */
+    public void checkServerTrusted(X509Certificate[] certificates,String authType) throws CertificateException {
+        if (LOG.isInfoEnabled() && certificates != null) {
+            for (int c = 0; c < certificates.length; c++) {
+                X509Certificate cert = certificates[c];
+                LOG.info(" Server certificate " + (c + 1) + ":");
+                LOG.info("  Subject DN: " + cert.getSubjectDN());
+                LOG.info("  Signature Algorithm: " + cert.getSigAlgName());
+                LOG.info("  Valid from: " + cert.getNotBefore() );
+                LOG.info("  Valid until: " + cert.getNotAfter());
+                LOG.info("  Issuer: " + cert.getIssuerDN());
+            }
+        }
+    	// TODO: Implementar uma caixa de diálogo que pergunta para o usuário se ele quer aceitar o certificado do site
+        // Implementado com try/catch usando JOptionPanel
+
+        try {
+        	defaultTrustManager.checkServerTrusted(certificates,authType);
+        } catch (CertificateException e){
+        	//Object[] options = {"Aceitar Certificado", "Aceitar Permanentemente", "Cancelar"};
+            Object[] options = {"Aceitar Certificado", "Cancelar"};
+        	switch (JOptionPane.showOptionDialog(null, "Falha na validação do seguinte certificado:\n" +
+        			certificates[0].getSubjectX500Principal().getName(),
+        			"\nO que você quer fazer?",
+        			JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null,
+        			options, options[0]))
+        	{
+        		case 2:
+        			// Rejeita certificado!
+        			throw e;
+        		case 1:
+        			// Aceita certificado permanentemente
+        			// TODO: Adicionar código para inserir o certificado como um certificado confiável
+        			break;
+        			// Aceita certificado para esta sessão
+
+        	}
+        }
+    }
+
+    /**
+     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
+     */
+    public X509Certificate[] getAcceptedIssuers() {
+        return this.defaultTrustManager.getAcceptedIssuers();
+    }
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/CertificateParams.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/CertificateParams.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/CertificateParams.java	(revision 1182)
@@ -0,0 +1,13 @@
+package br.gov.serpro.cert;
+
+import java.util.Properties;
+
+public class CertificateParams {
+
+
+
+	public static void setParam(String param){
+
+	}
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/TokenCollection.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/TokenCollection.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/cert/TokenCollection.java	(revision 1182)
@@ -0,0 +1,50 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package br.gov.serpro.cert;
+
+import br.gov.serpro.setup.Setup;
+import java.util.HashMap;
+
+/**
+ *
+ * @author esa
+ */
+class TokenCollection extends HashMap<String, Token>{
+    
+    private String preferedTokenKey;
+    private final Setup setup;
+
+    public TokenCollection(Setup setup){
+       
+        this.setup = setup;
+        this.addTokens(setup.getParameter("token"));
+
+    }
+
+    public void setPreferedToken(java.lang.String preferedTokenKey){
+        
+    }
+
+    public String getPreferedToken(){
+        return preferedTokenKey;
+    }
+
+    private void addTokens(String tokens){
+        
+        String[] tokensArray = tokens.split(",");
+        for (String tokenString : tokensArray){
+            if (tokenString != null && tokenString.length() > 0){
+                String[] tokenArray = tokenString.split(";");
+                Token token = new Token(tokenArray[0], tokenArray[1], this.setup);
+                token.registerToken();
+                if (token.isRegistered()){
+                    this.put(token.getName(), token);
+                }
+            }
+        }
+    }
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/ui/DialogBuilder.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/ui/DialogBuilder.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/ui/DialogBuilder.java	(revision 1182)
@@ -0,0 +1,582 @@
+package br.gov.serpro.ui;
+
+import br.gov.serpro.setup.Setup;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.lang.reflect.InvocationTargetException;
+
+import java.util.List;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.WindowConstants;
+
+public final class DialogBuilder extends JDialog implements PropertyChangeListener{
+
+	private Setup setup;
+    private static Object lock = new Object();
+	private String pin = new String();
+    private String certificateSubject = new String();
+    //private List<String> certificatelist = new ArrayList<String>();
+	private boolean ok = false;
+	private boolean locked = true;
+	private JOptionPane optionPane;
+    
+//	private DialogBuilder myself;
+	private EventManager em = new EventManager();
+
+	/**
+	 * Gerado automaticamente
+	 */
+	private static final long serialVersionUID = 1003857725229120014L;
+
+    private int dialogType = -1;
+
+	//JPanel pNorte, pCentro, pSul;
+	//JButton btOk, btCancel;
+	//JLabel lTitle, lTitle2, lPin;
+	JPasswordField pfPin;
+    JList lCertificatesList;
+
+    public static final int PIN_NEEDED_DIALOG = 0;
+    public static final int CERTICATE_SELECTOR_DIALOG = 1;
+
+	private DialogBuilder(Frame parent, Setup setup){
+
+		super(parent, true);
+        this.setup = setup;
+//		this.myself = this;
+
+		//this.setVisible(true);
+
+	}
+
+	private void buildPinDialog(){
+
+        this.dialogType = DialogBuilder.PIN_NEEDED_DIALOG;
+
+        this.setContentPane(new JPanel());
+		this.setLayout(new BorderLayout());
+		this.setTitle(this.setup.getLang("ExpressoCertMessages", "pin"));
+		this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+		this.setModal(true);
+
+		JPanel pNorte = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pNorte, BorderLayout.NORTH);
+		JPanel pCentro = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pCentro, BorderLayout.CENTER);
+		JPanel pSul = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pSul, BorderLayout.SOUTH);
+
+		//JLabel lTitle = new JLabel("Entre com o seu PIN:");
+        JLabel lTitle = new JLabel(this.setup.getLang("ExpressoCertMessages", "DialogBuilder001"));
+		pNorte.add(lTitle);
+
+		JLabel lPin = new JLabel(this.setup.getLang("ExpressoCertMessages", "pin")+":");
+		this.pfPin = new JPasswordField(30);
+		this.pfPin.requestFocusInWindow();
+		this.pfPin.addKeyListener(this.em);
+		pCentro.add(lPin);
+		pCentro.add(this.pfPin);
+
+		JButton btOk = new JButton(this.setup.getLang("ExpressoCertMessages", "ok"));
+		btOk.setMnemonic(KeyEvent.VK_ENTER);
+		btOk.setActionCommand("ok");
+
+		btOk.addActionListener(this.em);
+
+		JButton btCancel = new JButton(this.setup.getLang("ExpressoCertMessages", "cancel"));
+		btCancel.setMnemonic(KeyEvent.VK_ESCAPE);
+		btCancel.setActionCommand("cancel");
+		btCancel.addActionListener(this.em);
+
+		pSul.add(btOk);
+		pSul.add(btCancel);
+
+		this.addWindowListener(this.em);
+		this.pack();
+
+		//Posicionando no centro da tela.
+		Dimension mySize = this.getSize();
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		//System.out.println("ScreenSize: " + screenSize.toString()+"\nMySize: " +mySize.toString());
+		this.setLocation(screenSize.width/2 - (mySize.width/2), screenSize.height/2 - (mySize.height/2));
+
+		setVisible(true);
+		//this.repaint();
+
+	}
+
+    /*
+	private void buildPinNotNeededDialog(){
+
+        this.setContentPane(new JPanel());
+		this.setLayout(new BorderLayout());
+		this.setTitle("PIN");
+		this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+		this.setModal(true);
+
+		JPanel pNorte = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pNorte, BorderLayout.NORTH);
+		JPanel pCentro = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pCentro, BorderLayout.CENTER);
+		JPanel pSul = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pSul, BorderLayout.SOUTH);
+
+		JLabel lTitle = new JLabel("Token inserido e desbloqueado");
+		pNorte.add(lTitle);
+
+		JLabel lTitle2 = new JLabel("foi detectado");
+		pCentro.add(lTitle2);
+
+		JButton btOk = new JButton("Ok");
+		btOk.setMnemonic(KeyEvent.VK_ENTER);
+		btOk.setActionCommand("prosseguir");
+		btOk.addActionListener(em);
+
+		pSul.add(btOk);
+
+		this.addWindowListener(em);
+		this.pack();
+
+		//Posicionando no centro da tela.
+		Dimension mySize = this.getSize();
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		//System.out.println("ScreenSize: " + screenSize.toString()+"\nMySize: " +mySize.toString());
+		this.setLocation(screenSize.width/2 - (mySize.width/2), screenSize.height/2 - (mySize.height/2));
+
+		setVisible(true);
+		//this.repaint();
+	}
+     */
+
+    private void buildCertificateSelector(List<String> subjectList){
+
+        dialogType = DialogBuilder.CERTICATE_SELECTOR_DIALOG;
+
+        this.setContentPane(new JPanel());
+        this.setLayout(new BorderLayout());
+        this.setTitle(this.setup.getLang("ExpressoCertMessages", "certificate"));
+        this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+		this.setModal(true);
+
+        JPanel pNorte = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pNorte, BorderLayout.NORTH);
+		JPanel pCentro = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pCentro, BorderLayout.CENTER);
+		JPanel pSul = new JPanel(new FlowLayout(FlowLayout.CENTER));
+		this.add(pSul, BorderLayout.SOUTH);
+
+        JLabel lTitle = new JLabel(this.setup.getLang("ExpressoCertMessages", "DialogBuilder002"));
+        pNorte.add(lTitle);
+
+        lCertificatesList = new JList(subjectList.toArray());
+        lCertificatesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        pCentro.add(lCertificatesList);
+
+        JButton bSelect = new JButton(this.setup.getLang("ExpressoCertMessages", "select"));
+        bSelect.setMnemonic(KeyEvent.VK_ENTER);
+		bSelect.setActionCommand("selecionar");
+		bSelect.addActionListener(em);
+        pSul.add(bSelect);
+
+        JButton btCancel = new JButton(this.setup.getLang("ExpressoCertMessages", "cancel"));
+        btCancel.setMnemonic(KeyEvent.VK_ESCAPE);
+		btCancel.setActionCommand("cancel");
+		btCancel.addActionListener(this.em);
+        pSul.add(btCancel);
+
+        this.addWindowListener(em);
+		this.pack();
+
+    }
+
+	private void cancelButtonActionPerformed(){
+		this.setVisible(false);
+		this.pin = null; // hack para saber que diálogo foi cancelado.
+		this.unlock();
+		//this.dispose();
+	}
+
+	private void okButtonActionPerformed(){
+		this.setVisible(false);
+		this.unlock();
+		//this.dispose();
+	}
+
+	protected String getPin() {
+			return this.pin;
+	}
+
+	protected void setPin(String pin) {
+		this.pin = pin;
+	}
+
+    protected String getCertificateSubject() {
+        return certificateSubject;
+    }
+
+    protected void setCertificateSubject(String certificateSubject) {
+        this.certificateSubject = certificateSubject;
+    }
+
+    protected boolean isLocked(){
+		synchronized (DialogBuilder.lock) {
+			return this.locked;
+		}
+	}
+
+	private void setLocked(boolean locked) {
+		synchronized (DialogBuilder.lock) {
+			this.locked = locked;
+		}
+
+	}
+
+	private void unlock(){
+		synchronized (DialogBuilder.lock) {
+			setLocked(false);
+			DialogBuilder.lock.notifyAll();
+		}
+	}
+
+	static public int showMessageDialog(Frame parent, Object message, Setup setup){
+
+		return DialogBuilder.showDialog(parent, message, JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, setup);
+
+	}
+
+	static public int showMessageDialog(Frame parent, Object message, int messageType, Setup setup){
+
+		return DialogBuilder.showDialog(parent, message, messageType, JOptionPane.DEFAULT_OPTION, setup);
+
+	}
+
+	static public int showConfirmDialog(Frame parent, Object message, int messageType, int optionType, Setup setup){
+
+		return DialogBuilder.showDialog(parent, message, messageType, optionType, setup);
+
+	}
+
+	static public int showDialog(Frame parent, Object message, int messageType, int optionType, Setup setup){
+
+		DialogBuilder dialog = new DialogBuilder(parent, setup);
+		int valor = dialog.buildDialog(message, messageType, optionType);
+
+		dialog.dispose();
+
+		return valor;
+
+/*
+		JOptionPane optionPane = new JOptionPane(message, messageType, optionType);
+		JDialog dialog = new JDialog(parent, true);
+		dialog.setContentPane(optionPane);
+		optionPane.addPropertyChangeListener(this);
+		dialog.setTitle("Teste");
+		dialog.pack();
+
+		//Posicionando no centro da tela.
+		Dimension mySize = dialog.getSize();
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		dialog.setLocation(screenSize.width/2 - (mySize.width/2), screenSize.height/2 - (mySize.height/2));
+		dialog.setVisible(true);
+
+		Object selectedValue = optionPane.getValue();
+		int resultado = JOptionPane.CLOSED_OPTION;
+		if (selectedValue != null){
+			resultado = JOptionPane.CLOSED_OPTION;
+		}
+		else if (selectedValue instanceof Integer){
+			resultado =  ((Integer)selectedValue).intValue();
+		}
+
+		dialog.dispose();
+
+		return resultado;
+*/
+	}
+
+	private int buildDialog(Object message, int messageType, int optionType){
+
+		this.optionPane = new JOptionPane(message, messageType, optionType);
+		this.setModal(true);
+		this.setContentPane(this.optionPane);
+		this.optionPane.addPropertyChangeListener(this);
+		//this.setTitle("Teste");
+		this.pack();
+
+		//Posicionando no centro da tela.
+		Dimension mySize = this.getSize();
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		this.setLocation(screenSize.width/2 - (mySize.width/2), screenSize.height/2 - (mySize.height/2));
+		this.setVisible(true);
+
+		Object selectedValue = this.optionPane.getValue();
+		int resultado = JOptionPane.CLOSED_OPTION;
+		if (selectedValue != null){
+			resultado = JOptionPane.CLOSED_OPTION;
+		}
+		else if (selectedValue instanceof Integer){
+			resultado =  ((Integer)selectedValue).intValue();
+		}
+
+		//this.dispose();
+
+		return resultado;
+
+	}
+
+	public void propertyChange(PropertyChangeEvent evt) {
+		// TODO Stub de método gerado automaticamente
+		String property = evt.getPropertyName();
+
+		if (this.isVisible() &&
+				evt.getSource() == optionPane &&
+				JOptionPane.VALUE_PROPERTY.equals(property)){
+
+			this.setVisible(false);
+		}
+
+	}
+
+	static public String showPinDialog(Frame parent, Setup setup) {
+		DialogBuilder pinCodeDialog = new DialogBuilder(parent, setup);
+
+		try {
+			SwingUtilities.invokeAndWait(pinCodeDialog.new PinCodeNeededBuilder());
+		} catch (InterruptedException e1) {
+			// TODO Bloco catch gerado automaticamente
+			e1.printStackTrace();
+		} catch (InvocationTargetException e1) {
+			// TODO Bloco catch gerado automaticamente
+			e1.printStackTrace();
+		}
+
+		synchronized (DialogBuilder.lock) {
+
+			while (pinCodeDialog.isLocked()){
+				try {
+					DialogBuilder.lock.wait();
+				} catch (InterruptedException e) {
+					// TODO Bloco catch gerado automaticamente
+				}
+			}
+
+			String pin = pinCodeDialog.getPin();
+			pinCodeDialog.dispose();
+			pinCodeDialog = null;
+
+			return pin;
+		}
+	}
+
+    /*
+	static public boolean showPinNotNeededDialog(Frame parent){
+		DialogBuilder pinCodeDialog = new DialogBuilder(parent);
+
+		try {
+			SwingUtilities.invokeAndWait(pinCodeDialog.new PinCodeNotNeededBuilder());
+		} catch (InterruptedException e1) {
+			// TODO Bloco catch gerado automaticamente
+			e1.printStackTrace();
+		} catch (InvocationTargetException e1) {
+			// TODO Bloco catch gerado automaticamente
+			e1.printStackTrace();
+		}
+
+		synchronized (DialogBuilder.lock) {
+
+			while (pinCodeDialog.isLocked()){
+				try {
+					DialogBuilder.lock.wait();
+				} catch (InterruptedException e) {
+					// TODO Bloco catch gerado automaticamente
+				}
+			}
+
+			boolean ok = pinCodeDialog.isOk();
+			pinCodeDialog.dispose();
+			pinCodeDialog = null;
+
+			return ok;
+		}
+	}
+     */
+
+	/*
+	synchronized static public boolean showTryAgainDialog(Frame parent) {
+		//JOptionPane(Object message, int messageType, int optionType);
+		JOptionPane tryAgainOptionPane = new JOptionPane("Não foi possível carregar dispositivo " +
+				"ou arquivo de  chaves privadas. Tentar novamente?",
+				JOptionPane.INFORMATION_MESSAGE, JOptionPane.YES_NO_OPTION);
+
+		JDialog tryAgainDialog = tryAgainOptionPane.createDialog(parent, "");
+		tryAgainDialog.setContentPane(tryAgainOptionPane);
+		tryAgainDialog.setVisible(true);
+
+
+
+		try {
+			SwingUtilities.invokeAndWait(pinCodeDialog.new PinCodeNotNeededBuilder());
+		} catch (InterruptedException e1) {
+			// TODO Bloco catch gerado automaticamente
+			e1.printStackTrace();
+		} catch (InvocationTargetException e1) {
+			// TODO Bloco catch gerado automaticamente
+			e1.printStackTrace();
+		}
+
+		while (pinCodeDialog.isLocked()){
+			try {
+				pinCodeDialog.wait();
+			} catch (InterruptedException e) {
+				// TODO Bloco catch gerado automaticamente
+			}
+		}
+
+		boolean tryAgain = pinCodeDialog.isOk();
+		pinCodeDialog.dispose();
+		pinCodeDialog = null;
+
+		return tryAgain;
+
+	}
+	*/
+
+	synchronized private boolean isOk() {
+		return ok;
+	}
+
+	synchronized private void setOk(boolean ok) {
+		this.ok = ok;
+	}
+
+	private class PinCodeNeededBuilder implements Runnable {
+
+		public void run() {
+			buildPinDialog();
+		}
+	}
+
+    /*
+	private class PinCodeNotNeededBuilder implements Runnable {
+
+		public void run() {
+			buildPinNotNeededDialog();
+		}
+	}
+     */
+
+	private final class EventManager implements ActionListener, KeyListener, WindowListener{
+
+		public void keyPressed(KeyEvent keyEvt) {
+			int keyPressed = keyEvt.getKeyCode();
+
+			if (keyPressed == KeyEvent.VK_ENTER){
+				//System.out.println("Tecla ENTER pressionada");
+                if (lCertificatesList != null){
+                    // é uma lista de certificados
+                }
+                else {
+                    this.actionPerformed(new ActionEvent(keyEvt.getSource(), keyEvt.getID(), "ok"));
+                }
+			}
+			else if (keyPressed == KeyEvent.VK_ESCAPE){
+				//System.out.println("Tecla ESC pressionada");
+				this.actionPerformed(new ActionEvent(keyEvt.getSource(), keyEvt.getID(), "cancel"));
+			}
+		}
+
+		public void keyReleased(KeyEvent arg0) {
+		}
+
+		public void keyTyped(KeyEvent keyEvt) {
+		}
+
+		public void windowActivated(WindowEvent arg0) {
+		}
+
+		public void windowClosed(WindowEvent arg0) {
+			//System.out.println("Window Closed: Fechando diálogo!");
+		}
+
+		public void windowClosing(WindowEvent arg0) {
+			//System.out.println("Window Closing: Fechando diálogo!");
+			cancelButtonActionPerformed();
+		}
+
+		public void windowDeactivated(WindowEvent arg0) {
+		}
+
+		public void windowDeiconified(WindowEvent arg0) {
+		}
+
+		public void windowIconified(WindowEvent arg0) {
+		}
+
+		public void windowOpened(WindowEvent arg0) {
+		}
+
+		public void actionPerformed(ActionEvent evt) {
+
+			String command = evt.getActionCommand();
+
+			// Atribui o valor digitado para o campo
+			// senha e esconde a interface e assina o certificado
+			if (command.equals("ok")){
+
+				//if (pfPin != null && pfPin.getPassword().length <= 0){
+					// Mostra um aviso de que o campo PIN não foi preenchido
+					//JOptionPane.showMessageDialog(null, "Campo PIN não foi preenchido!",
+					//		"Aviso", JOptionPane.INFORMATION_MESSAGE);
+				//}
+				//else {
+					setPin(String.valueOf(pfPin.getPassword()));
+					okButtonActionPerformed();
+				//}
+
+			}
+			else if (command.equals("cancel")){
+				cancelButtonActionPerformed();
+			}
+			else if (command.equals("prosseguir")){
+				setOk(true);
+				okButtonActionPerformed();
+			}
+		}
+
+/*		private Frame findParentFrame(Component source){
+
+			Component parent = source.getParent();
+
+			while (parent != null){
+				if (parent instanceof Frame){
+					return (Frame) parent;
+				}
+			}
+
+			return (Frame) null;
+
+
+		}
+*/
+	}
+
+}
Index: /branches/2.0/security/ExpressoCert/src/br/gov/serpro/util/Base64Utils.java
===================================================================
--- /branches/2.0/security/ExpressoCert/src/br/gov/serpro/util/Base64Utils.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/src/br/gov/serpro/util/Base64Utils.java	(revision 1182)
@@ -0,0 +1,172 @@
+package br.gov.serpro.util;
+
+public class Base64Utils {
+
+    private static byte[] mBase64EncMap, mBase64DecMap;
+
+    /**
+     * Class initializer. Initializes the Base64 alphabet (specified in RFC-2045).
+     */
+    static {
+        byte[] base64Map = {
+            (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F',
+            (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L',
+            (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R',
+            (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X',
+            (byte)'Y', (byte)'Z',
+            (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f',
+            (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l',
+            (byte)'m', (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r',
+            (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x',
+            (byte)'y', (byte)'z',
+            (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
+            (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' };
+        mBase64EncMap = base64Map;
+        mBase64DecMap = new byte[128];
+        for (int i=0; i<mBase64EncMap.length; i++)
+            mBase64DecMap[mBase64EncMap[i]] = (byte) i;
+    }
+
+    /**
+     * This class isn't meant to be instantiated.
+     */
+    private Base64Utils() {
+    }
+
+    /**
+     * Encodes the given byte[] using the Base64-encoding,
+     * as specified in RFC-2045 (Section 6.8).
+     *
+     * @param aData the data to be encoded
+     * @return the Base64-encoded <var>aData</var>
+     * @exception IllegalArgumentException if NULL or empty array is passed
+     */
+    public static String base64Encode(byte[] aData) {
+        if ((aData == null) || (aData.length == 0))
+            throw new IllegalArgumentException("Can not encode NULL or empty byte array.");
+
+        byte encodedBuf[] = new byte[((aData.length+2)/3)*4];
+
+        // 3-byte to 4-byte conversion
+        int srcIndex, destIndex;
+        for (srcIndex=0, destIndex=0; srcIndex < aData.length-2; srcIndex += 3) {
+            encodedBuf[destIndex++] = mBase64EncMap[(aData[srcIndex] >>> 2) & 077];
+            encodedBuf[destIndex++] = mBase64EncMap[(aData[srcIndex+1] >>> 4) & 017 |
+                        (aData[srcIndex] << 4) & 077];
+            encodedBuf[destIndex++] = mBase64EncMap[(aData[srcIndex+2] >>> 6) & 003 |
+                        (aData[srcIndex+1] << 2) & 077];
+            encodedBuf[destIndex++] = mBase64EncMap[aData[srcIndex+2] & 077];
+        }
+
+        // Convert the last 1 or 2 bytes
+        if (srcIndex < aData.length) {
+            encodedBuf[destIndex++] = mBase64EncMap[(aData[srcIndex] >>> 2) & 077];
+            if (srcIndex < aData.length-1) {
+                encodedBuf[destIndex++] = mBase64EncMap[(aData[srcIndex+1] >>> 4) & 017 |
+                    (aData[srcIndex] << 4) & 077];
+                encodedBuf[destIndex++] = mBase64EncMap[(aData[srcIndex+1] << 2) & 077];
+            }
+            else {
+                encodedBuf[destIndex++] = mBase64EncMap[(aData[srcIndex] << 4) & 077];
+            }
+        }
+
+        // Add padding to the end of encoded data
+        while (destIndex < encodedBuf.length) {
+            encodedBuf[destIndex] = (byte) '=';
+            destIndex++;
+        }
+
+        String result = new String(encodedBuf);
+        return result;
+    }
+
+
+    /**
+     * Decodes the given Base64-encoded data,
+     * as specified in RFC-2045 (Section 6.8).
+     *
+     * @param aData the Base64-encoded aData.
+     * @return the decoded <var>aData</var>.
+     * @exception IllegalArgumentException if NULL or empty data is passed
+     */
+    public static byte[] base64Decode(String aData) {
+        if ((aData == null) || (aData.length() == 0))
+            throw new IllegalArgumentException("Can not decode NULL or empty string.");
+
+        byte[] data = aData.getBytes();
+
+        // Skip padding from the end of encoded data
+        int tail = data.length;
+        while (data[tail-1] == '=')
+            tail--;
+
+        byte decodedBuf[] = new byte[tail - data.length/4];
+
+        // ASCII-printable to 0-63 conversion
+        for (int i = 0; i < data.length; i++)
+            data[i] = mBase64DecMap[data[i]];
+
+        // 4-byte to 3-byte conversion
+        int srcIndex, destIndex;
+        for (srcIndex = 0, destIndex=0; destIndex < decodedBuf.length-2;
+                srcIndex += 4, destIndex += 3) {
+            decodedBuf[destIndex] = (byte) ( ((data[srcIndex] << 2) & 255) |
+                ((data[srcIndex+1] >>> 4) & 003) );
+            decodedBuf[destIndex+1] = (byte) ( ((data[srcIndex+1] << 4) & 255) |
+                ((data[srcIndex+2] >>> 2) & 017) );
+            decodedBuf[destIndex+2] = (byte) ( ((data[srcIndex+2] << 6) & 255) |
+                (data[srcIndex+3] & 077) );
+        }
+
+        // Handle last 1 or 2 bytes
+        if (destIndex < decodedBuf.length)
+            decodedBuf[destIndex] = (byte) ( ((data[srcIndex] << 2) & 255) |
+                ((data[srcIndex+1] >>> 4) & 003) );
+        if (++destIndex < decodedBuf.length)
+            decodedBuf[destIndex] = (byte) ( ((data[srcIndex+1] << 4) & 255) |
+                ((data[srcIndex+2] >>> 2) & 017) );
+
+        return decodedBuf;
+    }
+
+    public static String der2pem(byte[] der){
+
+    	int begin = 0;
+    	int lineSize = 64;
+    	int end = lineSize;
+    	int bytesWriten = 0;
+
+
+    	String LF = "\n";
+    	String beginCertificate = "-----BEGIN CERTIFICATE-----" +LF;
+    	String endCertificate = "-----END CERTIFICATE-----";
+
+    	String base64encoded = Base64Utils.base64Encode(der);
+
+    	StringBuilder sb = new StringBuilder();
+    	sb.append(beginCertificate);
+
+
+    	do {
+
+    		if (base64encoded.length() - begin < end - begin){
+    			end = base64encoded.length();
+    		}
+
+    		String subs = base64encoded.substring(begin, end);
+    		sb.append(subs + LF);
+    		bytesWriten += end - begin;
+    		begin = end;
+    		end += lineSize;
+
+    	} while (bytesWriten != base64encoded.length());
+
+    	sb.append(endCertificate);
+
+    	return sb.toString();
+
+
+    }
+
+}
Index: /branches/2.0/security/ExpressoCert/build.xml
===================================================================
--- /branches/2.0/security/ExpressoCert/build.xml	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/build.xml	(revision 1182)
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="ExpressoCert_trunk" default="default" basedir=".">
+    <description>Builds, tests, and runs the project ExpressoCert_trunk.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be 
+    used for execution of your tasks. These targets are usually executed 
+    before and after some main targets. They are: 
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported 
+    nbproject/build-impl.xml file. 
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are: 
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
+      run:                      execution of project 
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="ExpressoCert_trunk-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on 
+    the compile target as the regular run target does. Again, for a list of available 
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file. 
+
+    -->
+</project>
Index: /branches/2.0/security/ExpressoCert/nbbuild.xml
===================================================================
--- /branches/2.0/security/ExpressoCert/nbbuild.xml	(revision 1182)
+++ /branches/2.0/security/ExpressoCert/nbbuild.xml	(revision 1182)
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="ExpressoCert_trunk" default="default" basedir=".">
+    <description>Builds, tests, and runs the project ExpressoCert_trunk.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be 
+    used for execution of your tasks. These targets are usually executed 
+    before and after some main targets. They are: 
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported 
+    nbproject/build-impl.xml file. 
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are: 
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
+      run:                      execution of project 
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="ExpressoCert_trunk-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on 
+    the compile target as the regular run target does. Again, for a list of available 
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file. 
+
+    -->
+</project>
Index: /branches/2.0/security/crl_admin/crl_admin.py
===================================================================
--- /branches/2.0/security/crl_admin/crl_admin.py	(revision 1182)
+++ /branches/2.0/security/crl_admin/crl_admin.py	(revision 1182)
@@ -0,0 +1,112 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import os, popen2, fcntl, select, time
+from string import replace
+
+theOutput = [] 
+
+def fazlog(dados):
+	for i in dados:
+		aux = i.split('\n')
+		for x in aux:
+			logging.info(x)
+
+def makeNonBlocking(fd): 
+     fl = fcntl.fcntl(fd, fcntl.F_GETFL) 
+     try: 
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NDELAY) 
+     except AttributeError: 
+        fcntl.fcntl(fd, fcntl.F_SETFL, fl | fcntl.FNDELAY) 
+  
+def ExeCmd(command): 
+    theOutput = [] 
+    child = popen2.Popen3(command, 1)      	# Captura a stdout e a stderr do comando... 
+    child.tochild.close()                             	# NÃ£o estamos "falando(escrevendo para a stdin do comando) com o processo filho... 
+    outfile = child.fromchild 
+    outfd = outfile.fileno() 
+    errfile = child.childerr 
+    errfd = errfile.fileno() 
+    makeNonBlocking(outfd)                          	# NÃ£o queremos um deadlock !!!!( Dica do Python Cookbook v1)
+    makeNonBlocking(errfd) 
+    outdata = errdata = '' 
+    outeof = erreof = 0 
+    while 1: 
+        ready = select.select([outfd,errfd],[],[]) # Aguarda a entrada(saida do comando) 
+        if outfd in ready[0]: 
+           outchunk = outfile.read() 
+           if outchunk == '': outeof = 1 
+           outdata = outdata + outchunk 
+        if errfd in ready[0]: 
+           errchunk = errfile.read() 
+           if errchunk == '': erreof = 1 
+           errdata = errdata + errchunk 
+        if outeof and erreof: break 
+        select.select([],[],[],.1)                         	#Da um tempo para os buffers serem preenchidosl 
+    err = child.wait() 
+    #if err != 0: 
+    #   raise RuntimeError, '%s failed w/ exit code %d\n%s' % (command, err, errdata) 
+    theOutput.append(outdata) 
+    theOutput.append(errdata) 
+    return theOutput 
+
+# No arquivo crl_admin_confg esta o path (arquivo) que aponta para a configuracao das urls(CRL_urls) das crls que devem ser obtidas.
+# Este arquivo tambem informa o arquivo onde sera gravada a log(log), e o arquivo contendo 
+# a cadeia de certificados para verificar as crls(CAfile).
+# Apos o import, executar a funcao ler_conf() .....
+from crl_admin_confg import *
+
+import logging
+
+# Configura como sera a log .....
+logging.basicConfig(level=logging.INFO,
+                    format='%(asctime)s %(levelname)s %(message)s',
+                    filename=log,
+                    filemode='a')
+
+# A execucao da funcao a seguir carrega a lista das CRLs (CRL_urls) que devem ser processadas...
+ler_conf()
+
+for crl in CRL_urls:
+        arquivo = crl[1] + os.path.split(crl[0])[1]
+	url = crl[0] 
+
+        # Obtendo o arquivo com wget ... 
+	logging.info('Buscando a CRL: ' + url)
+	saida_wget = ExeCmd("wget " + url + " -O " + arquivo)
+
+	# Abaixo estamos comandando a execucao "openssl crl" passando o arquivo crl .
+	# O resultado esta em saida. Tem de ser 'verify OK' .
+	if os.path.exists(arquivo) and os.path.getsize(arquivo) > 0:
+		logging.info('Verificando ' + arquivo + '(' + str(os.path.getsize(arquivo)) + ' Bytes)')
+		saida = ExeCmd('openssl crl -CAfile ' + CAfile + '  -in ' + arquivo + ' -inform DER -noout')
+		# usa a funcao fazlog porque saida he um array.....
+		fazlog(saida)
+		try:
+			aux1 = replace(saida[1],'\n','')
+			if not aux1 == 'verify OK':
+				logging.critical('Erro verificando a CRL ' + arquivo + '\n')
+				try:
+					os.remove(arquivo)
+				except:
+					pass
+				continue
+                        # A crl foi verificada e est OK.... 
+			# Atuaiza a data do arquivo...
+			saida = ExeCmd('touch ' + arquivo)
+		except:
+			logging.critical('Erro processando o status da verificacao da CRL ' + arquivo + '\n')
+			try:
+				os.remove(arquivo)
+			except:
+				pass
+			continue
+	else:
+		# usa a funcao fazlog porque saida_wget he um array.....
+                fazlog(saida_wget) 
+		logging.critical('Nao foi possivel obter a CRL ' + url + '\n')
+		try:
+			os.remove(arquivo)
+		except:
+			pass
+
Index: /branches/2.0/security/crl_admin/crl_admin_confg.py
===================================================================
--- /branches/2.0/security/crl_admin/crl_admin_confg.py	(revision 1182)
+++ /branches/2.0/security/crl_admin/crl_admin_confg.py	(revision 1182)
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+
+# Lista com as urls das CRLs, e onde colocar(path) os arquivos obtidos ....
+CRL_urls = []
+
+# arquivo : path para o arquivo de configuracao contendo urls das crls e paths onde serao baixadas...
+arquivo = '/var/www/expresso/security/crl_admin/crl_admin.conf'
+		   
+# Arquivo onde sera grada log de execucao da qtualizacao/verificacao das crls.
+log = '/var/www/expresso/logs/arquivo_crls.log'
+
+# Deixe 'log' igual a vazio para ver as msgs de execuÃ§Ã£o no terminal.......
+#log = ''
+
+# Arquivo com cadeia dos certificados das CAs, para verificacao das CRLs.
+CAfile = '/var/www/expresso/security/cas/todos.cer'
+
+def ler_conf():
+        # Esta funcao le o arquivo passado como parametro e gera a lista CRL_urls.
+        # O arquivo he esperado no formato:
+	# url;path       ( url = aponta onde buscar a crl,  e path aponta onde salvar a crl obtida. 
+	e = open(arquivo)
+	r = e.read()
+	aux1 = r.split('\n')
+	for linha in aux1:
+		if linha[0:1] != '#':
+			aux2 = linha.split(';')
+			if aux2[0] != '':
+				CRL_urls.append(aux2)
+	return
Index: /branches/2.0/security/crl_admin/crl_admin.conf
===================================================================
--- /branches/2.0/security/crl_admin/crl_admin.conf	(revision 1182)
+++ /branches/2.0/security/crl_admin/crl_admin.conf	(revision 1182)
@@ -0,0 +1,21 @@
+# Lista das urls apontando CRLs, e onde colocar(path) os arquivos obtidos ....
+# Formato:
+# url;path   ( o path deve terminar com uma / )
+http://ccd.serpro.gov.br/lcr/serproacfv1.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/serproacfv2.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/ACPRv1.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/ACPRv2.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/ACSERPRORFB.crl;/var/www/expresso/security/crls/
+http://acraiz.icpbrasil.gov.br/LCRacraizv1.crl;/var/www/expresso/security/crls/
+http://acraiz.icpbrasil.gov.br/LCRacraiz.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/acserpro.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/acserprojus.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/acserprorfb.crl;/var/www/expresso/security/crls/
+http://ccd.serpro.gov.br/lcr/acserprosrf.crl;/var/www/expresso/security/crls/
+http://icp.caixa.gov.br/repositorio/ACCAIXA1.crl;/var/www/expresso/security/crls/
+http://icp.caixa.gov.br/repositorio/ACCAIXAPF1.crl;/var/www/expresso/security/crls/
+http://icp.caixa.gov.br/repositorio/ACCAIXAPJ1.crl;/var/www/expresso/security/crls/
+http://icp.caixa.gov.br/repositorio/ACCAIXAJUS.crl;/var/www/expresso/security/crls/
+http://ccdhom.serpro.gov.br/lcr/homacraizv2.crl;/var/www/expresso/security/crls/
+http://ccdhom.serpro.gov.br/lcr/homserprov2.crl;/var/www/expresso/security/crls/
+http://ccdhom.serpro.gov.br/lcr/acserprorfbv1.crl;/var/www/expresso/security/crls/
Index: /branches/2.0/security/ExpressoCertMail/manifest.mf
===================================================================
--- /branches/2.0/security/ExpressoCertMail/manifest.mf	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/manifest.mf	(revision 1182)
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
Index: /branches/2.0/security/ExpressoCertMail/src/br/gov/serpro/js/ParamReaderThread.java
===================================================================
--- /branches/2.0/security/ExpressoCertMail/src/br/gov/serpro/js/ParamReaderThread.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/src/br/gov/serpro/js/ParamReaderThread.java	(revision 1182)
@@ -0,0 +1,216 @@
+package br.gov.serpro.js;
+
+import java.awt.Frame;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.ProviderException;
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import org.bouncycastle.mail.smime.SMIMEException;
+import br.gov.serpro.cert.DigitalCertificate;
+import br.gov.serpro.setup.Setup;
+import br.gov.serpro.ui.DialogBuilder;
+import br.gov.serpro.util.Base64Utils;
+
+import netscape.javascript.JSObject;
+import org.bouncycastle.cms.CMSException;
+
+
+public class ParamReaderThread extends Thread {
+
+	JSObject page;
+	Javascript2AppletPassingData data;
+	Setup setup;
+	Frame parentFrame;
+
+	public ParamReaderThread(JSObject page, Javascript2AppletPassingData data, Setup setup, Frame parent) {
+	//public ParamReaderThread(JSObject page, Javascript2AppletPassingData data, Setup setup) {
+		super();
+		this.page = page;
+		this.data = data;
+		this.setup = setup;
+		this.parentFrame = parent;
+	}
+
+	@Override
+	public void run() {
+		// TODO Stub de método gerado automaticamente
+		super.run();
+
+        while (true){
+			if (setup.getParameter("debug").equalsIgnoreCase("true")){
+				System.out.println("Classe ParamReaderThread: pegando resultado.");
+			}
+            
+            // processa o smime. Método sign implementado na classe DigitalCertificate
+            String smime = null;
+            DigitalCertificate dc = null;
+
+            try {
+
+                //Map<String, String> parsedData = parseData(resultado);
+                Map<String, String> parsedData = data.getMap();
+
+                dc = new DigitalCertificate(this.parentFrame, this.setup);
+                dc.init();
+
+                // Testa a operação e se for
+                if (parsedData.get("operation").equals("sign")){
+
+                    smime = dc.signMail(parsedData);
+                    if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                        System.out.println("\nMensagem assinada: " + smime);
+                    }
+
+                }
+              else if (parsedData.get("operation").equals("decript")){
+                    String decryptedMsg = dc.cipherMail(parsedData);
+                    if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                        System.out.println("Mensagem decifrada: " + decryptedMsg);
+                    }
+                    if (decryptedMsg == null){
+                        smime = null;
+                    } else {
+                        smime = Base64Utils.base64Encode(decryptedMsg.getBytes());
+                    }
+                    
+                }
+                else {
+                    throw new UnsupportedOperationException("Operation not supported: " + parsedData.get("operation"));
+                    // Lança
+                }
+
+                // Retorna para a página
+                // se smime = null, a assinatura não funcionou
+                if (smime != null){
+                    page.call("appletReturn", new String[] {smime, parsedData.get("ID"), parsedData.get("operation"), parsedData.get("folder")});
+                }
+
+            } catch (IOException e) {
+                //DialogBuilder.showMessageDialog(this.parentFrame, "Não foi possível carregar Token/SmartCard: senha incorreta", this.setup);
+                DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMailMessages", "ParamReaderThread001"), this.setup);
+                //JOptionPane.showMessageDialog(this.parentFrame, "Não foi possível carregar Token/SmartCard: senha incorreta",
+                //        "Aviso", JOptionPane.INFORMATION_MESSAGE);
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+            } catch (GeneralSecurityException e) {
+                if (e.getCause() != null){
+                    DialogBuilder.showMessageDialog(this.parentFrame, "GeneralSecurityException: " + e.getCause().getMessage(), this.setup);
+                    //JOptionPane.showMessageDialog(this.parentFrame, "GeneralSecurityException: " + e.getCause().getMessage(),
+                    //        "Aviso", JOptionPane.INFORMATION_MESSAGE);
+                }
+                else {
+                    DialogBuilder.showMessageDialog(this.parentFrame, "GeneralSecurityException: " + e.getMessage(), this.setup);
+                    //JOptionPane.showMessageDialog(this.parentFrame, "GeneralSecurityException: " + e.getMessage(),
+                    //        "Aviso", JOptionPane.INFORMATION_MESSAGE);
+                }
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+            } catch (SMIMEException e) {
+                //DialogBuilder.showMessageDialog(this.parentFrame, "Erro no processamento da assinatura: " + e.getMessage(), this.setup);
+                DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMailMessages", "ParamReaderThread002"), this.setup);
+                //JOptionPane.showMessageDialog(this.parentFrame, "Erro no processamento da assinatura: " + e.getMessage(),
+                //        "Aviso", JOptionPane.INFORMATION_MESSAGE);
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+            } catch (MessagingException e) {
+                //DialogBuilder.showMessageDialog(this.parentFrame, "Erro no processamento da mensagem: " + e.getMessage(), this.setup);
+                DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMailMessages", "ParamReaderThread003"), this.setup);
+                //JOptionPane.showMessageDialog(this.parentFrame, "Erro no processamento da mensagem: " + e.getMessage(),
+                //        "Aviso", JOptionPane.INFORMATION_MESSAGE);
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+            }
+            catch (CMSException e){
+                //DialogBuilder.showMessageDialog(this.parentFrame, "Erro ao decifrar mensagem: Detectado problema na integridade da mensagem cifrada!", this.setup);
+                DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMailMessages", "ParamReaderThread004"), this.setup);
+                //if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                Throwable cause = e.getCause();
+                System.out.println(e.getClass().getCanonicalName() + ": " + e.getMessage());
+                if (cause != null){
+                    System.out.println(cause.getClass().getCanonicalName() + ": " + cause.getMessage());
+                }
+                e.printStackTrace();
+                //}
+            }
+            catch (ProviderException e){
+                //DialogBuilder.showMessageDialog(this.parentFrame, "Problema no acesso às informações do Token: " + e.getMessage(), this.setup);
+                DialogBuilder.showMessageDialog(this.parentFrame, setup.getLang("ExpressoCertMailMessages", "ParamReaderThread005"), this.setup);
+                //JOptionPane.showMessageDialog(this.parentFrame, "Problema no acesso às informações do Token: " + e.getMessage(),
+                //        "Aviso", JOptionPane.INFORMATION_MESSAGE);
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+            }
+            catch (UnsupportedOperationException e){
+                // DialogBuilder.showMessageDialog(this.parentFrame, e.getMessage());
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+            }
+            catch (IllegalArgumentException e){
+                //DialogBuilder.showMessageDialog(this.parentFrame, e.getMessage());
+                if (setup.getParameter("debug").equalsIgnoreCase("true")) {
+                    e.printStackTrace();
+                }
+            }
+            catch (InterruptedException e){
+                if (setup.getParameter("debug").equalsIgnoreCase("true")){
+                    System.out.println("Classe ParamReaderThread: Thread has been interrupted! Break.");
+                    e.printStackTrace();
+                }
+                break;
+            }
+
+			dc.destroy();
+			dc = null;
+			System.gc();
+            Thread.yield();
+
+        }
+	}
+
+	//TODO: Documentar o que recebe!!!
+	/*private Map<String, String> parseData(String expressoMailData){
+
+        if ((expressoMailData == null) || (expressoMailData.length() == 0)){
+            throw new IllegalArgumentException("Can't unserialize NULL or empty value!");
+        }
+
+		Map<String, String> parsedData = new HashMap<String, String> ();
+		//Map<String, String> headersData = new HashMap<String, String>();
+
+		for (String paramsArray : expressoMailData.split(";")){
+
+			if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+				System.out.println("sendo parseado: " + paramsArray);
+			}
+			String[] param = paramsArray.split(":");
+			//if (temp[0].equals("headers")){
+			//	String[] headersArray = new String(Base64Utils.base64Decode(temp[1])).split(";");
+			//	for (String header: headersArray){
+			//		String[] keyValueType = header.split(":");
+			//		headersData.put(keyValueType[0], keyValueType[1]);
+			//	}
+			//}
+			//else{
+			parsedData.put(param[0], new String(Base64Utils.base64Decode(param[1])));
+			//}
+
+		}
+
+		return parsedData;
+	}
+
+	static private String processReturn(Map smimeData){
+
+		return new String();
+	}
+*/
+}
Index: /branches/2.0/security/ExpressoCertMail/src/br/gov/serpro/js/Javascript2AppletPassingData.java
===================================================================
--- /branches/2.0/security/ExpressoCertMail/src/br/gov/serpro/js/Javascript2AppletPassingData.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/src/br/gov/serpro/js/Javascript2AppletPassingData.java	(revision 1182)
@@ -0,0 +1,67 @@
+package br.gov.serpro.js;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Javascript2AppletPassingData {
+
+	private boolean locked = true;
+	private String data = null;
+    private Map<String, String> mapData = null;
+
+	public synchronized String getData() throws InterruptedException{
+		if (isLocked()){
+			data = null;
+            wait();
+		}
+		lock();
+		//notifyAll();
+		return this.data;
+	}
+
+	public synchronized Map<String, String> getMap() throws InterruptedException{
+		if (isLocked()){
+			mapData = null;
+            wait();
+		}
+		lock();
+		//notifyAll();
+		return this.mapData;
+	}
+
+	public synchronized void setData(String data){
+		//flag = true;
+		this.data = data;
+		unlock();
+	}
+
+    public synchronized void setData(String operation, String id, String body){
+		//flag = true;
+        if (this.mapData == null) {
+            this.mapData = new HashMap<String, String>();
+        }
+		this.mapData.put("operation", operation);
+        this.mapData.put("ID", id);
+        this.mapData.put("body", body);
+		unlock();
+	}
+
+    public synchronized void setData(String operation, String id, String body, String folder){
+        this.mapData = new HashMap<String, String>();
+        this.mapData.put("folder", folder);
+        this.setData(operation, id, body);
+    }
+
+	public synchronized void unlock(){
+		locked = false;
+		notifyAll();
+	}
+
+	private synchronized void lock(){
+		locked = true;
+	}
+
+	public synchronized boolean isLocked(){
+		return locked;
+	}
+}
Index: /branches/2.0/security/ExpressoCertMail/src/Main.java
===================================================================
--- /branches/2.0/security/ExpressoCertMail/src/Main.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/src/Main.java	(revision 1182)
@@ -0,0 +1,82 @@
+import java.applet.Applet;
+
+import netscape.javascript.JSObject;
+
+import br.gov.serpro.cert.DigitalCertificate;
+import br.gov.serpro.setup.Setup;
+
+
+public class Main extends Applet{
+
+	//private DigitalCertificate teste;
+	private Thread executaTeste;
+	private DataReader dataReader;
+	private JSObject page;
+	private Setup setup;
+
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = 1726731542858100340L;
+
+	/**
+	 *
+	 */
+	public void start() {
+		this.setup = new Setup(this);
+		//this.teste = new DigitalCertificate();
+		this.dataReader = new DataReader();
+		//this.data.setResultado(this.teste.init());
+		this.executaTeste = new Leitor(dataReader, JSObject.getWindow(this), setup);
+		this.executaTeste.start();
+
+		//this.page = JSObject.getWindow(this);
+		/*try{
+			SwingUtilities.invokeAndWait(new Runnable(){
+
+				public void run() {
+					// TODO Auto-generated method stub
+					int resultado = teste.init();
+					System.out.println("Event Dispatching Thread: Resultado apÃ³s leitura do token" + resultado);
+					dataReader.setResultado(resultado);
+				}
+
+			});
+		}
+		catch (InterruptedException e){
+			e.printStackTrace();
+		}
+		catch (InvocationTargetException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		*/
+	}
+
+	//TODO: Testar a passagem de parÃ¢metros do javascript para a applet
+	/**
+	 * @param args
+	 */
+	public void doButtonClickAction(String resultado){
+		dataReader.setResultado(resultado);
+		dataReader.unlock();
+		//return "cert";
+	}
+
+	//public class Executor extends Thread {
+		//
+	//	@Override
+	//	public void run() {
+	//		// TODO Auto-generated method stub
+	//		super.run();
+//
+	//		// chamar funÃ§Ã£o no javascript
+	//		System.out.println("Classe executor: pegando resultado.");
+	//		int resultado = dataReader.getResultado();
+	//		page.call("mostraResultado", new String[] {Integer.toString(resultado)});
+
+	//	}
+
+	//}
+
+}
Index: /branches/2.0/security/ExpressoCertMail/src/ExpressoSmimeApplet.java
===================================================================
--- /branches/2.0/security/ExpressoCertMail/src/ExpressoSmimeApplet.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/src/ExpressoSmimeApplet.java	(revision 1182)
@@ -0,0 +1,90 @@
+import java.awt.Frame;
+
+import javax.swing.JApplet;
+import javax.swing.SwingUtilities;
+
+import netscape.javascript.JSObject;
+
+import br.gov.serpro.js.Javascript2AppletPassingData;
+import br.gov.serpro.js.ParamReaderThread;
+import br.gov.serpro.setup.Setup;
+
+//TODO: Logs de depuração e interface para usuário reportar problemas.
+
+public class ExpressoSmimeApplet extends JApplet {
+
+	/**
+	 * Variáveis de instância
+	 */
+	private Setup setup;
+	private Javascript2AppletPassingData dataReader;
+	private Thread paramReaderThread;
+
+	/**
+	 * Variáveis de classe
+	 */
+	private static final long serialVersionUID = 4797603392324194391L;
+
+	@Override
+	public void init() {
+		super.init();
+		this.setSize(1, 1);
+		this.setup = new Setup(this);
+        this.setup.addLanguageResource("ExpressoCertMailMessages");
+		this.dataReader = new Javascript2AppletPassingData();
+		// this.paramReaderThread = new ParamReaderThread(JSObject.getWindow(this), dataReader, setup);
+		this.paramReaderThread = new ParamReaderThread(JSObject.getWindow(this), dataReader,
+				setup, (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this));
+		this.paramReaderThread.start();
+	}
+
+	@Override
+    /**
+     * Retorna Informações sobre os parâmetros que essa applet aceita
+     * @author Mário César Kolling <mario.kolling@serpro.gov.br>
+     * @return String[][] Uma matriz de Strings relacionando cada parâmetro à sua descrição
+     */
+	public String[][] getParameterInfo() {
+		return setup.getParameterInfo();
+	}
+
+	@Override
+	public void start() {
+		super.start();
+
+	}
+
+	@Override
+	public void stop() {
+		super.stop();
+		//dataReader.unlock();
+
+        if (this.paramReaderThread.isAlive()){
+            if (setup.getParameter("debug").equalsIgnoreCase("true")){
+                System.out.println("Interrompendo Applet paramReaderThread");
+            }
+            this.paramReaderThread.interrupt();
+        }
+	}
+
+	/**
+     * Método da Applet chamado pela página (js) ao assinar ou decifrar um e-mail
+	 * @param resultado Dados serializados passados pela Applet
+	 */
+	public void doButtonClickAction(String resultado){
+		dataReader.setData(resultado);
+		dataReader.unlock();
+		//return "cert";
+	}
+
+    public void doButtonClickAction(String operation, String id, String body){
+        dataReader.setData(operation, id, body);
+        //dataReader.unlock();
+    }
+
+    public void doButtonClickAction(String operation, String id, String body, String folder){
+        dataReader.setData(operation, id, body, folder);
+        //dataReader.unlock();
+    }
+
+}
Index: /branches/2.0/security/ExpressoCertMail/src/DataReader.java
===================================================================
--- /branches/2.0/security/ExpressoCertMail/src/DataReader.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/src/DataReader.java	(revision 1182)
@@ -0,0 +1,33 @@
+
+public class DataReader {
+
+	private boolean flag = false;
+	private String resultado;
+
+	public synchronized String getResultado(){
+		lock();
+		//notifyAll();
+		return this.resultado;
+	}
+
+	public synchronized void setResultado(String resultado){
+		//flag = true;
+		this.resultado = resultado;
+	}
+
+	public synchronized void unlock(){
+		flag = true;
+		notifyAll();
+	}
+
+	private synchronized void lock(){
+		while (!flag){
+			try{
+				wait();
+			}
+			catch (InterruptedException e){}
+		}
+		flag = false;
+	}
+
+}
Index: /branches/2.0/security/ExpressoCertMail/src/Leitor.java
===================================================================
--- /branches/2.0/security/ExpressoCertMail/src/Leitor.java	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/src/Leitor.java	(revision 1182)
@@ -0,0 +1,37 @@
+import br.gov.serpro.setup.Setup;
+import netscape.javascript.JSObject;
+
+
+class Leitor extends Thread {
+
+	private DataReader dataReader;
+	private JSObject page;
+	private Setup setup;
+
+	public Leitor(DataReader dataReader, JSObject page, Setup setup){
+		this.dataReader = dataReader;
+		this.page = page;
+		this.setup = setup;
+	}
+
+	public void run() {
+		// TODO Auto-generated method stub
+		super.run();
+		// chamar funÃ§Ã£o no javascript
+		while (true){
+			if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+				System.out.println("Classe executor: pegando resultado.");
+			}
+
+			String resultado = dataReader.getResultado();
+			if (this.setup.getParameter("debug").equalsIgnoreCase("true")){
+				System.out.println("Classe Executor: chamando funÃ§Ã£o appletReturn() no javascript");
+				System.out.println("Classe Executor: valor de retorno: " + resultado);
+			}
+
+			page.call("appletReturn", new String[] {resultado});
+
+		}
+	}
+
+}
Index: /branches/2.0/security/ExpressoCertMail/testapplet.html
===================================================================
--- /branches/2.0/security/ExpressoCertMail/testapplet.html	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/testapplet.html	(revision 1182)
@@ -0,0 +1,39 @@
+<html>
+	<head>
+		<title>Applet de Teste para Criptografia</title>
+	</head>
+	<body>
+
+		<form id="form1">
+		<script language="Javascript">
+			if (navigator.userAgent.match('MSIE')){
+				document.write('<object style="display:yes" id="cert_applet"' +
+					'classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" width="300" height="125">' +
+					'<param name="type" value="application/x-java-applet;version=1.5">' +
+					'<param name="code" value="Main.class">' +
+					'<param name="mayscript" value="true">' +
+					'<param name="archive" value="ExpressoCertMail.jar,bcmail-jdk15-140.jar">');
+			}
+			else {
+				document.write('<embed id="cert_applet" code="Main.class" archive="ExpressoCertMail.jar,bcmail-jdk15-140.jar"' +
+					'type="application/x-java-applet;version=1.5" width="300" height="125" mayscript >' +
+					'<noembed>' +
+					'No Java Support.' +
+					'</noembed>' +
+					'</embed>');
+			}
+		</script>
+		<input type="button" id="botao" value="Mostra" onclick="buttonClick();"/>
+		</form>
+		<script language="javascript">
+			function buttonClick(){
+				var applet = document.getElementById('cert_applet');
+				applet.doButtonClickAction();
+			}
+
+			function mostraResultado(resultado){
+				alert(resultado);
+			}
+		</script>
+	</body>
+</html>
Index: /branches/2.0/security/ExpressoCertMail/build.xml
===================================================================
--- /branches/2.0/security/ExpressoCertMail/build.xml	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/build.xml	(revision 1182)
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="ExpressoCertMail_trunk" default="default" basedir=".">
+    <description>Builds, tests, and runs the project ExpressoCertMail_trunk.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be 
+    used for execution of your tasks. These targets are usually executed 
+    before and after some main targets. They are: 
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported 
+    nbproject/build-impl.xml file. 
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are: 
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
+      run:                      execution of project 
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="ExpressoCertMail_trunk-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on 
+    the compile target as the regular run target does. Again, for a list of available 
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file. 
+
+    -->
+</project>
Index: /branches/2.0/security/ExpressoCertMail/nbbuild.xml
===================================================================
--- /branches/2.0/security/ExpressoCertMail/nbbuild.xml	(revision 1182)
+++ /branches/2.0/security/ExpressoCertMail/nbbuild.xml	(revision 1182)
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="ExpressoCertMail_trunk" default="default" basedir=".">
+    <description>Builds, tests, and runs the project ExpressoCertMail_trunk.</description>
+    <import file="nbproject/build-impl.xml"/>
+    <!--
+
+    There exist several targets which are by default empty and which can be 
+    used for execution of your tasks. These targets are usually executed 
+    before and after some main targets. They are: 
+
+      -pre-init:                 called before initialization of project properties
+      -post-init:                called after initialization of project properties
+      -pre-compile:              called before javac compilation
+      -post-compile:             called after javac compilation
+      -pre-compile-single:       called before javac compilation of single file
+      -post-compile-single:      called after javac compilation of single file
+      -pre-compile-test:         called before javac compilation of JUnit tests
+      -post-compile-test:        called after javac compilation of JUnit tests
+      -pre-compile-test-single:  called before javac compilation of single JUnit test
+      -post-compile-test-single: called after javac compilation of single JUunit test
+      -pre-jar:                  called before JAR building
+      -post-jar:                 called after JAR building
+      -post-clean:               called after cleaning build products
+
+    (Targets beginning with '-' are not intended to be called on their own.)
+
+    Example of inserting an obfuscator after compilation could look like this:
+
+        <target name="-post-compile">
+            <obfuscate>
+                <fileset dir="${build.classes.dir}"/>
+            </obfuscate>
+        </target>
+
+    For list of available properties check the imported 
+    nbproject/build-impl.xml file. 
+
+
+    Another way to customize the build is by overriding existing main targets.
+    The targets of interest are: 
+
+      -init-macrodef-javac:     defines macro for javac compilation
+      -init-macrodef-junit:     defines macro for junit execution
+      -init-macrodef-debug:     defines macro for class debugging
+      -init-macrodef-java:      defines macro for class execution
+      -do-jar-with-manifest:    JAR building (if you are using a manifest)
+      -do-jar-without-manifest: JAR building (if you are not using a manifest)
+      run:                      execution of project 
+      -javadoc-build:           Javadoc generation
+      test-report:              JUnit report generation
+
+    An example of overriding the target for project execution could look like this:
+
+        <target name="run" depends="ExpressoCertMail_trunk-impl.jar">
+            <exec dir="bin" executable="launcher.exe">
+                <arg file="${dist.jar}"/>
+            </exec>
+        </target>
+
+    Notice that the overridden target depends on the jar target and not only on 
+    the compile target as the regular run target does. Again, for a list of available 
+    properties which you can use, check the target you are overriding in the
+    nbproject/build-impl.xml file. 
+
+    -->
+</project>
Index: /branches/2.0/security/classes/Verifica_Certificado_conf.php
===================================================================
--- /branches/2.0/security/classes/Verifica_Certificado_conf.php	(revision 1182)
+++ /branches/2.0/security/classes/Verifica_Certificado_conf.php	(revision 1182)
@@ -0,0 +1,5 @@
+<?php
+$GLOBALS['dirtemp']  =   '/var/www/expresso/security/temp'; 
+$GLOBALS['CAs']  =         '/var/www/expresso/security/cas/todos.cer';
+$GLOBALS['CRLs']  =       '/var/www/expresso/security/crls/'; 
+?>
Index: /branches/2.0/security/classes/CertificadoB.php
===================================================================
--- /branches/2.0/security/classes/CertificadoB.php	(revision 1182)
+++ /branches/2.0/security/classes/CertificadoB.php	(revision 1182)
@@ -0,0 +1,271 @@
+<?php
+require_once('funcoes_auxiliares.php');
+require_once('Verifica_Certificado_conf.php');
+require_once('Verifica_Certificado.php');
+
+//  Pre-requisito: A extensao openssl deve estar disponivel no PHP ....
+class certificadoB
+{
+  public $dados = array();        # Area para armazenar os dados recuperados do certificado.
+  public $apresentado = false;    # Deve ser testado para verificar se no certificado processado foi localizado o CPF
+  public $erros_ssl = array(); 
+  public $cert_assinante = '';	
+  public $msg_sem_assinatura = '';  #conteï¿œdo da mensagem sem assinatura, que retorna da funï¿œï¿œo verify do openssl
+  public $arquivos_para_deletar = array();
+  
+  public function __construct()
+  {
+    if(!extension_loaded('openssl'))
+    {
+      #PHP sem suporte ao openssl.....
+      return false;
+    }
+    $this->dados = array();
+    $this->dados_xml = '';
+    $this->apresentado = false;
+    $this->erros_ssl = array();
+    $this->cert_assinante = '';
+    $this->msg_sem_assinatura = '';
+    $this->arquivos_para_deletar = array();
+  }
+
+public function __destruct()
+  {
+    #Remover arquivos temporarios.....	
+    deleta_arquivos_temporarios($this->arquivos_para_deletar);
+  }
+
+# Recupera dados de um certificado no formato pem 
+  function certificado($certificado_pem)
+  {		
+      if (!$certificado_pem) 
+    {
+        $this->apresentado = False;
+        return False;    # Sem parametro ...
+    }
+   $this->dados = recupera_dados_do_ceritificado_digital($certificado_pem);
+   $this->dados_xml = gera_xml_com_dados_do_certificado($this->dados);
+     # Certificado foi processado, as informacoes obtidas estao em $this->dados. 
+  $this->apresentado = true;  
+  }
+
+# Encriptar senha ......
+	public function encriptar_senha($s,$c)
+	{
+		if($s == '')	return false;
+		if($c == '')	return false;
+		$senha_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		//echo $senha_arquivo_temporario.'<br>';
+		if(!grava_arquivo($senha_arquivo_temporario,$s))
+		{
+			return false;
+		}
+		$cert_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		//echo $cert_arquivo_temporario.'<br>';
+		if(!grava_arquivo($cert_arquivo_temporario,$c))
+		{
+			return false;
+		}
+		$senha_criptografada_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		//echo $senha_criptografada_arquivo_temporario.'<br>';
+
+		$w = exec('openssl rsautl -in ' . $senha_arquivo_temporario . ' -out ' . $senha_criptografada_arquivo_temporario . ' -inkey ' . $cert_arquivo_temporario . ' -certin -pkcs -keyform PEM -encrypt',$saida);
+		
+		if(!file_exists($senha_criptografada_arquivo_temporario))
+		{
+			deleta_arquivos_temporarios($this->arquivos_para_deletar);
+			$this->arquivos_para_deletar = array();
+			return false;
+		}
+
+                # Recupera a senha criptada, binario.....		
+		$retorno = file_get_contents($senha_criptografada_arquivo_temporario);
+
+		deleta_arquivos_temporarios($this->arquivos_para_deletar);
+		$this->arquivos_para_deletar = array();
+
+		if(strlen($retorno) != 128) return false;
+
+		return base64_encode($retorno);
+	}
+
+
+# Encriptar uma msg($m) para o(s) destinatario(s) $c(array com certificados)com os headers passados em $h.
+	public function encriptar($m,$c,$h)
+	{
+		if($m == '')	return false;
+		if(!is_array($c))	return false;
+		$aux = count($c);
+		if($aux < 1)	return false;		
+		if(!is_array($h) )	return false;
+		# Tem de verificar todos os certificados que serao utilizados para criptografar a msg..
+		# Inserir a rotina aqui....
+		$m_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		//echo $m_arquivo_temporario.'<br>';
+		if(!grava_arquivo($m_arquivo_temporario,$m))
+		{
+			return false;
+		}
+		$enc_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		//echo $enc_arquivo_temporario.'<br>';
+		
+		// LIMPA ERROS ... Pode ser um problema para outras aplicacoes que usam openssl(fonte de erros unica).
+		while ($erro = openssl_error_string()); //  Limpa buffer de erros anteriores......
+		$this->erros_ssl = array();
+		$resultado = openssl_pkcs7_encrypt($m_arquivo_temporario,$enc_arquivo_temporario,$c,$h,$flags=0,$cipherid=OPENSSL_CIPHER_DES);
+		//$resultado = openssl_pkcs7_encrypt($m_arquivo_temporario,$enc_arquivo_temporario,$c,$h);
+		if(!$resultado)
+		{
+			// Guarda msgs de erro ...
+			while ($erro = openssl_error_string()) 
+			{ 
+				$this->erros_ssl[] = $erro;
+			}
+			deleta_arquivos_temporarios($this->arquivos_para_deletar);
+			$this->arquivos_para_deletar = array();
+			return false;
+		}
+                # Recupera a msg criptada......		
+		$retorno = file_get_contents($enc_arquivo_temporario);
+		deleta_arquivos_temporarios($this->arquivos_para_deletar);
+		$this->arquivos_para_deletar = array();
+		return $retorno;
+	}
+
+
+# Verifica uma msg($m) assinada... 
+	public function verificar($m)
+	{
+		if($m == '') return false;
+		$m_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		if(!grava_arquivo($m_arquivo_temporario,$m))
+		{
+			deleta_arquivos_temporarios($this->arquivos_para_deletar);
+			$this->arquivos_para_deletar = array();
+			return false;
+		}
+		$vrf_cert_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		$vrf_msg_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		// LIMPA ERROS ... Pode ser um problema para outras aplicacoes que usam openssl(fonte de erros unica).
+		while ($erro = openssl_error_string()); //  Limpa buffer de erros anteriores......
+		$this->erros_ssl = array();
+		$resultado = openssl_pkcs7_verify($m_arquivo_temporario,0, $vrf_cert_arquivo_temporario,array($GLOBALS['CAs']),$GLOBALS['CAs'],$vrf_msg_arquivo_temporario); 
+	        $retorno = true; 
+		if($resultado === false || $resultado == -1)
+		{
+		        # Indica ocorrencia de erro ... 
+		        $retorno = false;
+			// Guarda msgs de erro ...
+			while ($erro = openssl_error_string()) 
+			{ 
+				$this->erros_ssl[] = $erro;
+			}
+			# Reexecuta o comando com um nivel menor de criticas.
+			$this->cert_assinante = $this->extrai_certificado_da_msg_assinada($m);
+			if(!$this->cert_assinante)
+			{
+				# Se nao foi possivel obter o certificado retorna com falso .....
+				deleta_arquivos_temporarios($this->arquivos_para_deletar);
+				$this->arquivos_para_deletar = array();
+				return false;
+			}
+		}
+		else
+		{
+			# Guarda o certificado usado para assinar a msg
+			$this->cert_assinante  =   file_get_contents($vrf_cert_arquivo_temporario);
+                 }
+		If(file_exists($vrf_msg_arquivo_temporario))  $this->msg_sem_assinatura =  file_get_contents($vrf_msg_arquivo_temporario);
+		$this->certificado($this->cert_assinante);
+		if (!$this->apresentado)
+		{
+			$this->erros_ssl[] = 'Certificado da Msg nao pode ser tratado.';
+			$retorno = false;
+		}
+		else
+		{
+			while ($erro = openssl_error_string()); //  Limpa buffer de erros anteriores......
+			# Certificado poderia ter assinado um email?? 
+			if(!($this->dados['KEYUSAGE']['digitalSignature'] && $this->dados['EXTKEYUSAGE']['emailProtection']))
+			{
+				$this->erros_ssl[] = 'Certificado nao poderia ter sido utilizado para assinar email.';
+				while ($erro = openssl_error_string()) 
+					{ 
+						$this->erros_ssl[] = $erro;
+					}
+				$retorno = false;
+			}
+			$revogado = new Verifica_Certificado($this->dados,$this->cert_assinante);
+			$this->dados['REVOGADO'] = false;
+  			if(!$revogado->status){
+   				$this->erros_ssl[] = $revogado->msgerro;
+				foreach ($revogado->erros_ssl as $item)
+					{
+						$this->erros_ssl[] = $item;
+					}
+				if ($revogado->revogado)
+					$this->dados['REVOGADO'] = true;
+   				$retorno  = false;
+   			}
+		}
+		deleta_arquivos_temporarios($this->arquivos_para_deletar);
+		$this->arquivos_para_deletar = array();	
+		return $retorno;
+	}	
+
+	public function extrai_certificado_da_msg_assinada($m)
+	{
+	        $retorno = false;
+	        if(!$m)
+		{
+			return $retorno;
+		}
+		$m_arquivo_temporario = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		if(!grava_arquivo($m_arquivo_temporario,$m))
+		{
+			deleta_arquivos_temporarios($this->arquivos_para_deletar);
+			$this->arquivos_para_deletar = array();
+			return $retorno;
+		}
+		$w='';
+		$saida = array();
+		$w = exec('cat ' . $m_arquivo_temporario . ' | openssl smime -pk7out | openssl pkcs7 -print_certs',$saida);
+		if(!$w=='')
+		{
+			deleta_arquivos_temporarios($this->arquivos_para_deletar);
+			$this->arquivos_para_deletar = array();
+			return $retorno;
+		}
+		$aux1 = '';
+                // gera uma unica string com o conteudo retornado pelo comando...
+		foreach($saida as $linha)
+		{
+			$aux1 .= $linha.chr(0x0A);
+		}
+                // cria um array com os certificados retornados..
+		$aux2 = explode('-----BEGIN CERTIFICATE-----',$aux1);
+		array_shift($aux2);
+		// isolando certificados..
+		$aux5 = array();
+		foreach($aux2 as $item)
+		{
+			$aux3 = explode('-----END CERTIFICATE-----',$item);
+			$aux4 = '-----BEGIN CERTIFICATE-----' . $aux3[0] . '-----END CERTIFICATE-----';
+			$aux5[] = $aux4;
+		}
+		// Testa qual dos certificados nao he uma CA ....
+		foreach($aux5 as $item)
+		{
+			$Dados_cert = recupera_dados_do_ceritificado_digital($item);
+			if(!$Dados_cert['CA'])
+				{
+					$retorno = $item;
+					break;
+				}		
+		}
+		deleta_arquivos_temporarios($this->arquivos_para_deletar);
+		$this->arquivos_para_deletar = array();
+		return $retorno;
+	}
+}
+?>
Index: /branches/2.0/security/classes/Verifica_Certificado.php
===================================================================
--- /branches/2.0/security/classes/Verifica_Certificado.php	(revision 1182)
+++ /branches/2.0/security/classes/Verifica_Certificado.php	(revision 1182)
@@ -0,0 +1,138 @@
+<?php
+require_once('Verifica_Certificado_conf.php');
+require_once('funcoes_auxiliares.php');
+
+class Verifica_Certificado 
+{
+	public $msgerro = '';
+	public $revogado = false;
+	public $status = false;
+	public $certificado = '';
+	public $CRL = '';
+        public $erros_ssl = array();
+	public $arquivos_para_deletar = array();
+	
+	public function __construct($parametro1,$parametro2)
+	{	
+		//include('Verifica_Certificado_conf.php');
+	        # $parametro1 = array com dados do certificado... 
+		# $parametro2 = certificado no formato PEM ...
+		$this->status = false;
+		if(!verificaopenssl())
+		{
+			$this->msgerro = 'MSG005i - Modulo openssl nao disponivel no PHP.';
+			return false;
+		}
+		If(!$parametro2)
+		 {
+			$this->msgerro = 'MSG005 - Certificado nao informado.';
+			return false;		
+		 }
+		$this->certificado = $parametro2; 
+		//Pode existir mais de um local para obter a CRL. Vai tabalhar com o primeiro ....
+		$aux = explode('/',$parametro1['CRLDISTRIBUTIONPOINTS'][0]);
+		$this->CRL = $aux[count($aux)-1];	
+		if(!$this->cmd_openssl_verify($this->certificado))
+		 {
+			$this->msgerro = 'MSG010 - Erro verificando Expiracao/CAs do certificado.';
+			return false;		 
+		 }
+		if($this->Testa_se_Certificado_Revogado(trim($GLOBALS['CRLs'].$this->CRL),$parametro1['SERIALNUMBER']))
+		 {
+			$this->msgerro = 'MSG011 - Ocorreu erro validando o certificado.';
+			return false;		 
+		 }		 
+		$this->status = true;
+	}
+	
+  public function __destruct()
+  {
+		#Remover arquivos temporarios.....	
+		deleta_arquivos_temporarios(&$this->arquivos_para_deletar);
+  }
+  
+   private function cmd_openssl_verify($ZXZ)
+	{
+		$this->erros_ssl = array();
+		if(!is_file($GLOBALS['CAs']))
+		{
+			# Se arquivo com cas nao existe, assume certificado invalido..
+			$this->erros_ssl[]= 'Autoridade certificadora desconhecida.(CA-01)';  //'Arquivo CAs nï¿œo localizado.';
+			return false;
+		}
+		$arq = gera_nome_arquivo_temporario(&$this->arquivos_para_deletar);
+		if(!$arq)
+		 {
+			#Nao foi possivel gerar path(nome) para arquivo temporario...
+			$this->erros_ssl[]= 'Autoridade certificadora desconhecida.(CA-02)';  //'Arquivo CAs nï¿œo localizado.';
+			return false;
+		 }
+		$ret = grava_arquivo($arq,$this->certificado);
+		$saida = array();
+		$w = exec('openssl verify -CAfile '.$GLOBALS['CAs'].' '.$arq,$saida);
+		deleta_arquivos_temporarios(&$this->arquivos_para_deletar);
+		$this->arquivos_para_deletar = array();
+		//echo 'w= '.$w.'<br>';
+		//echo '<pre>';
+		//print_r($saida);
+		//echo '</pre>';
+		$aux = explode(' ',$w);	
+		if($aux[1] != 'OK')   // OK no primeiro item do array significa que o comando executaou OK... mas tem erro no certificado...
+		{
+			foreach($saida as $item)
+			{
+			        $aux = explode(':',$item);
+				if(isset($aux[1]))
+				{
+					$this->erros_ssl[] = trim($aux[1]);
+				}
+			}			
+			return false;
+		}
+		return true;
+	}
+	
+	private function Testa_se_Certificado_Revogado($pcrl,$serial)
+	{
+		$this->erros_ssl = array();
+		if(!file_exists($pcrl))
+			{
+				# Se arquivo com crls nao existe, assume certificado revogado..
+				$this->erros_ssl[]= 'Couldn\'t verify if certificate was revoked.(CD-01)';  //'Arquivo CRL nao localizado.';
+				return true;
+			}	
+			if(!is_file($pcrl))
+			{
+				# Se nao for um arquivo, assume certificado revogado..
+				$this->erros_ssl[]= 'Couldn\'t verify if certificate was revoked.(CD-01)';  //'Arquivo CRL nao localizado.';
+				return true;
+			}
+		$crl = file_get_contents($pcrl,true);
+		$cert_data = Crl_parseASN($crl);
+                # testa se crl expirada....	
+		if(gmdate('YmdHis') >= data_hora($cert_data[1][0][1][4][1]))
+		{
+			# Se crl expirada, assume certificado revogado..
+			$this->erros_ssl[]= 'Couldn\'t verify if certificate was revoked.(CD-02)';  //'Arquivo CRL expirado.';
+			return true;
+		}
+		if (!is_array($cert_data) || ($cert_data[0] == 'UNKNOWN')) 
+		{
+			# Se o primeiro parametro aponta uma crl invalida retorna como certificado revogado...
+			$this->erros_ssl[]='Couldn\'t verify if certificate was revoked.(CD-03)'; //Arquivo CRL invalido.
+			return true;
+		}
+		foreach($cert_data[1][0][1][5][1] as $item)
+		{
+			if($serial === $item[1][0][1])
+			{
+			#Certificado esta revogado mesmo !!!!! 
+			$this->erros_ssl[]='REVOKED Certificate.';
+			$this->revogado = true;
+			return true;				
+			}
+		}
+		return false;
+	}	
+ }	
+?>
Index: /branches/2.0/security/classes/funcoes_auxiliares.php
===================================================================
--- /branches/2.0/security/classes/funcoes_auxiliares.php	(revision 1182)
+++ /branches/2.0/security/classes/funcoes_auxiliares.php	(revision 1182)
@@ -0,0 +1,1775 @@
+<?php
+//  Conjunto de funcoes de apoio as Classes de tratamento do certificado digital.
+	
+		function pega_path($chaves,$linha)
+		{
+			if(!is_array($chaves) || !$linha)
+				{
+					return false;
+				}
+			$ret = false;
+			foreach($chaves as $chave)
+				{
+					if(substr($linha,0,strlen($chave)) == $chave)
+						{
+							$c = '';
+							if(strpos($linha,'\'') > 0)
+								{
+									$c = "'";
+								}
+							if(strpos($linha,'"') > 0)
+								{
+									$c = '"';
+								}
+							if(!$c)
+								{
+									break;
+								}
+							$data = explode($c,$linha);
+							$ret = $data[1];
+							break;
+						}
+				}
+			return $ret;
+		}
+	
+	function ler_certificados_CAS($path)
+		{
+			// Ler o arquivo contendo certificados das Cas.
+			// Retorna array com os certificados formato PEM ...
+			$retorno = array();		
+			if(file_exists($path))
+				{
+					$dados_do_arquivo = file_get_contents($path); 
+					$inicio = '-----BEGIN CERTIFICATE-----';
+					$fim = '-----END CERTIFICATE-----';
+					$aux1 = explode($inicio,$dados_do_arquivo);
+					foreach($aux1 as $aux2)
+						{
+							if($aux2)
+								{
+									$aux3 = explode($fim,$aux2);
+									$retorno[] =$inicio . $aux3[0] . $fim;
+								}
+						}
+				}
+			return $retorno;
+		}
+	
+	function gera_xml_com_dados_do_certificado($dados)
+		{
+		        // Recebe array com dados de um certificado e gera o xml com estes dados. 
+			// As chaves formam os tags e os dados  o conteudo do tag ....
+			$aux_xml = "<certificado>";
+			if(is_array($dados))
+				{
+					foreach($dados as $K => $valor)
+						{
+							if(!is_array($valor))
+								{
+									$aux_xml .= '  <' . $K . '>'.$valor.'</' . $K . '>'; 
+								}
+							else
+								{
+									$aux_xml .= '  <' . $K . '>';
+									foreach($valor as $KX => $valorx)
+										{
+											if(is_int($KX))  $KX = 'D' . $KX;
+											$aux_xml .= '  <' . $KX . '>'.$valorx.'</' . $KX . '>';
+										}
+									$aux_xml .= '  </' . $K . '>';
+								}
+						}
+				}
+			$aux_xml .= "</certificado>";
+			return  $aux_xml;
+		}
+		
+		
+	function data_hora ($valor) 
+	{
+		$year  = substr($valor, 0, 2);
+		$month = substr($valor, 2, 2);
+		$day   = substr($valor, 4, 2);
+		$hour  = substr($valor, 6, 2);
+		$min   = substr($valor, 8, 2);
+		$sec   = substr($valor, 10, 2);
+		return gmdate('YmdHis',gmmktime($hour, $min, $sec, $month, $day, $year));
+	}
+
+
+	function gera_nome_arquivo_temporario($tab_arqs)
+	{
+		//include('Verifica_Certificado_conf.php');
+		if(!is_dir($GLOBALS['dirtemp']))
+		{
+			#Se nao existe uma area de trabalho aborta a funcao..
+			return false;
+		}
+		$lista = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
+		$N = $lista[rand(0,count($lista)-1)].date('U').$lista[rand(0,count($lista)-1)].RAND(12345,9999999999).$lista[rand(0,count($lista)-1)].$lista[rand(0,count($lista)-1)].RAND(12345,9999999999).'.tmp';
+		$aux = $GLOBALS['dirtemp'].'/'.$N;;
+	        array_push($tab_arqs ,$aux); 
+		return  $aux;		
+	}
+
+
+	function grava_arquivo($arquivo,$dados)
+	{
+		$ret = file_put_contents($arquivo,$dados);
+		if(!$ret)
+		{
+			return false;
+		}
+		else
+			return $ret;
+	}
+	
+	
+	function deleta_arquivos_temporarios($tab_arqs)
+	{
+		foreach($tab_arqs as $arquivo )
+			{ 
+				if(file_exists($arquivo))
+					{
+						unlink($arquivo);
+					}
+			}	
+	}
+
+
+       	function verificaopenssl()
+	{
+		if(extension_loaded('openssl'))
+		{
+			return true;
+		}
+		else
+			return false;
+	}
+
+
+       	function print_hex($value)
+	{
+		$tab_val = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); 
+		for($A=0;$A<strlen($value);$A++)
+			{
+				$aux_parte_numerica =ord(substr($value,$A,1)) % 16;
+				$aux_parte_zona = (ord(substr($value,$A,1)) - $aux_parte_numerica) / 16;
+				$parte_numerica = $tab_val[$aux_parte_numerica];
+				$parte_zona = $tab_val[$aux_parte_zona];
+				$hex .=  $parte_zona.$parte_numerica;
+			}
+			return $hex;
+	}
+
+
+	function get_length($len,$bytes,$data)
+	{
+		$len = ord($data[1]);
+		$bytes = 0;
+		# Testa se tamanho menor/igual a 127 bytes..
+		# Neste caso $len  ja he o tamanho do conteudo
+		if ($len & 0x80) 
+			{
+				# Testa se tamanho indefinido (nao deve ocorrer em uma codificação DER.
+				if($len == chr(0x80))
+					{
+						# Tamanho indefinido, limitado por 0x0000h
+						$len = strpos($data,chr(0x00).chr(0x00));
+						$bytes = 0;
+					}
+				else					
+					{
+						# he tamanho definido. diz quantos bytes formam o tamanho.... 
+						$bytes = $len & 0x0f;
+						$len = 0;
+						for ($i = 0; $i < $bytes; $i++) 
+							{
+								$len = ($len << 8) | ord($data[$i + 2]);
+							}
+					}
+			}
+	}
+	
+	function xBase128($ab,$q,$flag )
+	{
+		$abc = $ab; 
+		if( $q > 127 )
+			{
+				$abc = xBase128($abc, floor($q / 128), 0 );
+			}
+		$q = $q % 128;
+		if( $flag)
+			{
+				$abc[] = $q;
+			}
+		else
+			{
+				$abc[] = 0x80 | $q;
+			}
+		return $abc;
+	}
+
+
+
+	function OIDtoHex($oid)
+	{
+		$abBinary = array();
+		$partes = explode('.',$oid);
+		$n = 0;
+		$b = 0;
+		for($n = 0; $n < count($partes); $n++)
+		{
+			if($n==0)
+				{
+					$b = 40 * $partes[$n];
+				}
+			elseif($n==1)
+				{
+					$b +=  $partes[$n];
+					$abBinary[] = $b;
+				}
+			else
+				{
+					$abBinary = xBase128($abBinary, $partes[$n], 1 );
+				}
+		}
+		$value =chr(0x06) . chr(count($abBinary));
+		foreach($abBinary as $item)
+		{
+			$value .= chr($item);
+		}
+		return $value;
+	}
+	
+	
+	
+        function Crl_parseASN($data,$context_especific = false)
+	{  
+        // Tabela de OIDs .
+               	$_oids = array(
+			'2.5.4.3' => 'CN',
+			'2.5.4.4' => 'Surname',
+			'2.5.4.6' => 'C',
+			'2.5.4.7' => 'Cidade',
+			'2.5.4.8' => 'Estatdo',
+			'2.5.4.9' => 'StreetAddress',
+			'2.5.4.10' => 'O',
+			'2.5.4.11' => 'OU',
+			'2.5.4.12' => 'Title',
+			'2.5.4.20' => 'TelephoneNumber',
+			'2.5.4.42' => 'GivenName',
+			'2.5.29.14' => 'id-ce-subjectKeyIdentifier',
+			'2.5.29.15' => 'id-ce-keyUsage',
+			'2.5.29.17' => 'id-ce-subjectAltName',
+			'2.5.29.19' => 'id-ce-basicConstraints',
+			'2.5.29.20' => 'id-ce-cRLNumber',
+			'2.5.29.31' => 'id-ce-CRLDistributionPoints',
+			'2.5.29.32' => 'id-ce-certificatePolicies',
+			'2.5.29.35' => 'id-ce-authorityKeyIdentifier',
+			'2.5.29.37' => 'id-ce-extKeyUsage',
+			'1.2.840.113549.1.9.1' => 'Email',
+			'1.2.840.113549.1.1.1' => 'RSAEncryption',
+			'1.2.840.113549.1.1.2' => 'md2WithRSAEncryption',
+			'1.2.840.113549.1.1.4' => 'md5withRSAEncryption',
+			'1.2.840.113549.1.1.5' => 'SHA-1WithRSAEncryption',
+			'1.2.840.10040.4.3' => 'id-dsa-with-sha-1',
+			'1.3.6.1.5.5.7.3.2' => 'id_kp_clientAuth',
+			'1.3.6.1.5.5.7.3.4' => 'id_kp_securityemail',	
+			'1.3.6.1.5.5.7.2.1' => 'id_certificatePolicies',
+			'2.16.840.1.113730.1.1' => 'netscape-cert-type',
+			'2.16.840.1.113730.1.2' => 'netscape-base-url',
+			'2.16.840.1.113730.1.3' => 'netscape-revocation-url',
+			'2.16.840.1.113730.1.4' => 'netscape-ca-revocation-url',
+			'2.16.840.1.113730.1.7' => 'netscape-cert-renewal-url',
+			'2.16.840.1.113730.1.8' => 'netscape-ca-policy-url',
+			'2.16.840.1.113730.1.12' => 'netscape-ssl-server-name',
+			'2.16.840.1.113730.1.13' => 'netscape-comment',
+			'2.16.76.1.2.1' => 'A1',	
+			'2.16.76.1.2.3' => 'A3',	
+			'2.16.76.1.2.1.16' => 'Certification Practice Statement pointer',
+			'2.16.76.1.3.1' => 'Dados do cert parte 1',
+			'2.16.76.1.3.5' => 'Dados do cert parte 2',
+			'2.16.76.1.3.6' => 'Dados do cert parte 3',
+			'0.9.2342.19200300.100.1.25' => ' domainComponent',
+			'1.2.36.68980861.1.1.10' => ' Signet pilot',
+			'1.2.36.68980861.1.1.11' => ' Signet intraNet',
+			'1.2.36.68980861.1.1.2' => ' Signet personal',
+			'1.2.36.68980861.1.1.20' => ' Signet securityPolicy',
+			'1.2.36.68980861.1.1.3' => ' Signet business',
+			'1.2.36.68980861.1.1.4' => ' Signet legal',
+			'1.2.36.75878867.1.100.1.1' => ' Certificates Australia policyIdentifier',
+			'1.2.752.34.1' => ' seis-cp',
+			'1.2.752.34.1.1' => ' SEIS certificatePolicy-s10',
+			'1.2.752.34.2' => ' SEIS pe',
+			'1.2.752.34.3' => ' SEIS at',
+			'1.2.752.34.3.1' => ' SEIS at-personalIdentifier',
+			'1.2.840.10040.2.1' => ' holdinstruction-none',
+			'1.2.840.10040.2.2' => ' holdinstruction-callissuer',
+			'1.2.840.10040.2.3' => ' holdinstruction-reject',
+			'1.2.840.10040.4.1' => ' dsa',
+			'1.2.840.10040.4.3' => ' dsaWithSha1',
+			'1.2.840.10045.1' => ' fieldType',
+			'1.2.840.10045.1.1' => ' prime-field',
+			'1.2.840.10045.1.2' => ' characteristic-two-field',
+			'1.2.840.10045.1.2.1' => ' ecPublicKey',
+			'1.2.840.10045.1.2.3' => ' characteristic-two-basis',
+			'1.2.840.10045.1.2.3.1' => ' onBasis',
+			'1.2.840.10045.1.2.3.2' => ' tpBasis',
+			'1.2.840.10045.1.2.3.3' => ' ppBasis',
+			'1.2.840.10045.2' => ' publicKeyType',
+			'1.2.840.10045.2.1' => ' ecPublicKey',
+			'1.2.840.10046.2.1' => ' dhPublicNumber',
+			'1.2.840.113533.7' => ' nsn',
+			'1.2.840.113533.7.65' => ' nsn-ce',
+			'1.2.840.113533.7.65.0' => ' entrustVersInfo',
+			'1.2.840.113533.7.66' => ' nsn-alg',
+			'1.2.840.113533.7.66.10' => ' cast5CBC',
+			'1.2.840.113533.7.66.11' => ' cast5MAC',
+			'1.2.840.113533.7.66.12' => ' pbeWithMD5AndCAST5-CBC',
+			'1.2.840.113533.7.66.13' => ' passwordBasedMac',
+			'1.2.840.113533.7.66.3' => ' cast3CBC',
+			'1.2.840.113533.7.67' => ' nsn-oc',
+			'1.2.840.113533.7.67.0' => ' entrustUser',
+			'1.2.840.113533.7.68' => ' nsn-at',
+			'1.2.840.113533.7.68.0' => ' entrustCAInfo',
+			'1.2.840.113533.7.68.10' => ' attributeCertificate',
+			'1.2.840.113549.1.1' => ' pkcs-1',
+			'1.2.840.113549.1.1.1' => ' rsaEncryption',
+			'1.2.840.113549.1.1.2' => ' md2withRSAEncryption',
+			'1.2.840.113549.1.1.3' => ' md4withRSAEncryption',
+			'1.2.840.113549.1.1.4' => ' md5withRSAEncryption',
+			'1.2.840.113549.1.1.5' => ' sha1withRSAEncryption',
+			'1.2.840.113549.1.1.6' => ' rsaOAEPEncryptionSET',
+			'1.2.840.113549.1.9.16.2.11' => 'SMIMEEncryptionKeyPreference',
+			'1.2.840.113549.1.12' => ' pkcs-12',
+			'1.2.840.113549.1.12.1' => ' pkcs-12-PbeIds',
+			'1.2.840.113549.1.12.1.1' => ' pbeWithSHAAnd128BitRC4',
+			'1.2.840.113549.1.12.1.2' => ' pbeWithSHAAnd40BitRC4',
+			'1.2.840.113549.1.12.1.3' => ' pbeWithSHAAnd3-KeyTripleDES-CBC',
+			'1.2.840.113549.1.12.1.4' => ' pbeWithSHAAnd2-KeyTripleDES-CBC',
+			'1.2.840.113549.1.12.1.5' => ' pbeWithSHAAnd128BitRC2-CBC',
+			'1.2.840.113549.1.12.1.6' => ' pbeWithSHAAnd40BitRC2-CBC',
+			'1.2.840.113549.1.12.10' => ' pkcs-12Version1',
+			'1.2.840.113549.1.12.10.1' => ' pkcs-12BadIds',
+			'1.2.840.113549.1.12.10.1.1' => ' pkcs-12-keyBag',
+			'1.2.840.113549.1.12.10.1.2' => ' pkcs-12-pkcs-8ShroudedKeyBag',
+			'1.2.840.113549.1.12.10.1.3' => ' pkcs-12-certBag',
+			'1.2.840.113549.1.12.10.1.4' => ' pkcs-12-crlBag',
+			'1.2.840.113549.1.12.10.1.5' => ' pkcs-12-secretBag',
+			'1.2.840.113549.1.12.10.1.6' => ' pkcs-12-safeContentsBag',
+			'1.2.840.113549.1.12.2' => ' pkcs-12-ESPVKID',
+			'1.2.840.113549.1.12.2.1' => ' pkcs-12-PKCS8KeyShrouding',
+			'1.2.840.113549.1.12.3' => ' pkcs-12-BagIds',
+			'1.2.840.113549.1.12.3.1' => ' pkcs-12-keyBagId',
+			'1.2.840.113549.1.12.3.2' => ' pkcs-12-certAndCRLBagId',
+			'1.2.840.113549.1.12.3.3' => ' pkcs-12-secretBagId',
+			'1.2.840.113549.1.12.3.4' => ' pkcs-12-safeContentsId',
+			'1.2.840.113549.1.12.3.5' => ' pkcs-12-pkcs-8ShroudedKeyBagId',
+			'1.2.840.113549.1.12.4' => ' pkcs-12-CertBagID',
+			'1.2.840.113549.1.12.4.1' => ' pkcs-12-X509CertCRLBagID',
+			'1.2.840.113549.1.12.4.2' => ' pkcs-12-SDSICertBagID',
+			'1.2.840.113549.1.12.5' => ' pkcs-12-OID',
+			'1.2.840.113549.1.12.5.1' => ' pkcs-12-PBEID',
+			'1.2.840.113549.1.12.5.1.1' => ' pkcs-12-PBEWithSha1And128BitRC4',
+			'1.2.840.113549.1.12.5.1.2' => ' pkcs-12-PBEWithSha1And40BitRC4',
+			'1.2.840.113549.1.12.5.1.3' => ' pkcs-12-PBEWithSha1AndTripleDESCBC',
+			'1.2.840.113549.1.12.5.1.4' => ' pkcs-12-PBEWithSha1And128BitRC2CBC',
+			'1.2.840.113549.1.12.5.1.5' => ' pkcs-12-PBEWithSha1And40BitRC2CBC',
+			'1.2.840.113549.1.12.5.1.6' => ' pkcs-12-PBEWithSha1AndRC4',
+			'1.2.840.113549.1.12.5.1.7' => ' pkcs-12-PBEWithSha1AndRC2CBC',
+			'1.2.840.113549.1.12.5.2' => ' pkcs-12-EnvelopingID',
+			'1.2.840.113549.1.12.5.2.1' => ' pkcs-12-RSAEncryptionWith128BitRC4',
+			'1.2.840.113549.1.12.5.2.2' => ' pkcs-12-RSAEncryptionWith40BitRC4',
+			'1.2.840.113549.1.12.5.2.3' => ' pkcs-12-RSAEncryptionWithTripleDES',
+			'1.2.840.113549.1.12.5.3' => ' pkcs-12-SignatureID',
+			'1.2.840.113549.1.12.5.3.1' => ' pkcs-12-RSASignatureWithSHA1Digest',
+			'1.2.840.113549.1.3' => ' pkcs-3',
+			'1.2.840.113549.1.3.1' => ' dhKeyAgreement',
+			'1.2.840.113549.1.5' => ' pkcs-5',
+			'1.2.840.113549.1.5.1' => ' pbeWithMD2AndDES-CBC',
+			'1.2.840.113549.1.5.10' => ' pbeWithSHAAndDES-CBC',
+			'1.2.840.113549.1.5.3' => ' pbeWithMD5AndDES-CBC',
+			'1.2.840.113549.1.5.4' => ' pbeWithMD2AndRC2-CBC',
+			'1.2.840.113549.1.5.6' => ' pbeWithMD5AndRC2-CBC',
+			'1.2.840.113549.1.5.9' => ' pbeWithMD5AndXOR',
+			'1.2.840.113549.1.7' => ' pkcs-7',
+			'1.2.840.113549.1.7.1' => ' data',
+			'1.2.840.113549.1.7.2' => ' signedData',
+			'1.2.840.113549.1.7.3' => ' envelopedData',
+			'1.2.840.113549.1.7.4' => ' signedAndEnvelopedData',
+			'1.2.840.113549.1.7.5' => ' digestData',
+			'1.2.840.113549.1.7.6' => ' encryptedData',
+			'1.2.840.113549.1.7.7' => ' dataWithAttributes',
+			'1.2.840.113549.1.7.8' => ' encryptedPrivateKeyInfo',
+			'1.2.840.113549.1.9' => ' pkcs-9',
+			'1.2.840.113549.1.9.1' => ' emailAddress',
+			'1.2.840.113549.1.9.10' => ' issuerAndSerialNumber',
+			'1.2.840.113549.1.9.11' => ' passwordCheck',
+			'1.2.840.113549.1.9.12' => ' publicKey',
+			'1.2.840.113549.1.9.13' => ' signingDescription',
+			'1.2.840.113549.1.9.14' => ' extensionReq',
+			'1.2.840.113549.1.9.15' => ' sMIMECapabilities',
+			'1.2.840.113549.1.9.15.1' => ' preferSignedData',
+			'1.2.840.113549.1.9.15.2' => ' canNotDecryptAny',
+			'1.2.840.113549.1.9.15.3' => ' receiptRequest',
+			'1.2.840.113549.1.9.15.4' => ' receipt',
+			'1.2.840.113549.1.9.15.5' => ' contentHints',
+			'1.2.840.113549.1.9.15.6' => ' mlExpansionHistory',
+			'1.2.840.113549.1.9.16' => ' id-sMIME',
+			'1.2.840.113549.1.9.16.0' => ' id-mod',
+			'1.2.840.113549.1.9.16.0.1' => ' id-mod-cms',
+			'1.2.840.113549.1.9.16.0.2' => ' id-mod-ess',
+			'1.2.840.113549.1.9.16.1' => ' id-ct',
+			'1.2.840.113549.1.9.16.1.1' => ' id-ct-receipt',
+			'1.2.840.113549.1.9.16.2' => ' id-aa',
+			'1.2.840.113549.1.9.16.2.1' => ' id-aa-receiptRequest',
+			'1.2.840.113549.1.9.16.2.2' => ' id-aa-securityLabel',
+			'1.2.840.113549.1.9.16.2.3' => ' id-aa-mlExpandHistory',
+			'1.2.840.113549.1.9.16.2.4' => ' id-aa-contentHint',
+			'1.2.840.113549.1.9.2' => ' unstructuredName',
+			'1.2.840.113549.1.9.20' => ' friendlyName',
+			'1.2.840.113549.1.9.21' => ' localKeyID',
+			'1.2.840.113549.1.9.22' => ' certTypes',
+			'1.2.840.113549.1.9.22.1' => ' x509Certificate',
+			'1.2.840.113549.1.9.22.2' => ' sdsiCertificate',
+			'1.2.840.113549.1.9.23' => ' crlTypes',
+			'1.2.840.113549.1.9.23.1' => ' x509Crl',
+			'1.2.840.113549.1.9.3' => ' contentType',
+			'1.2.840.113549.1.9.4' => ' messageDigest',
+			'1.2.840.113549.1.9.5' => ' signingTime',
+			'1.2.840.113549.1.9.6' => ' countersignature',
+			'1.2.840.113549.1.9.7' => ' challengePassword',
+			'1.2.840.113549.1.9.8' => ' unstructuredAddress',
+			'1.2.840.113549.1.9.9' => ' extendedCertificateAttributes',
+			'1.2.840.113549.2' => ' digestAlgorithm',
+			'1.2.840.113549.2.2' => ' md2',
+			'1.2.840.113549.2.4' => ' md4',
+			'1.2.840.113549.2.5' => ' md5',
+			'1.2.840.113549.3' => ' encryptionAlgorithm',
+			'1.2.840.113549.3.10' => ' desCDMF',
+			'1.2.840.113549.3.2' => ' rc2CBC',
+			'1.2.840.113549.3.3' => ' rc2ECB',
+			'1.2.840.113549.3.4' => ' rc4',
+			'1.2.840.113549.3.5' => ' rc4WithMAC',
+			'1.2.840.113549.3.6' => ' DESX-CBC',
+			'1.2.840.113549.3.7' => ' DES-EDE3-CBC',
+			'1.2.840.113549.3.8' => ' RC5CBC',
+			'1.2.840.113549.3.9' => ' RC5-CBCPad',
+			'1.2.840.113556.4.3' => ' microsoftExcel',
+			'1.2.840.113556.4.4' => ' titledWithOID',
+			'1.2.840.113556.4.5' => ' microsoftPowerPoint',
+			'1.3.133.16.840.9.84' => ' x9-84',
+			'1.3.133.16.840.9.84.0' => ' x9-84-Module',
+			'1.3.133.16.840.9.84.0.1' => ' x9-84-Biometrics',
+			'1.3.133.16.840.9.84.0.2' => ' x9-84-CMS',
+			'1.3.133.16.840.9.84.0.3' => ' x9-84-Identifiers',
+			'1.3.133.16.840.9.84.1' => ' biometric',
+			'1.3.133.16.840.9.84.1.0' => ' id-unknown-Type',
+			'1.3.133.16.840.9.84.1.1' => ' id-body-Odor',
+			'1.3.133.16.840.9.84.1.10' => ' id-palm',
+			'1.3.133.16.840.9.84.1.11' => ' id-retina',
+			'1.3.133.16.840.9.84.1.12' => ' id-signature',
+			'1.3.133.16.840.9.84.1.13' => ' id-speech-Pattern',
+			'1.3.133.16.840.9.84.1.14' => ' id-thermal-Image',
+			'1.3.133.16.840.9.84.1.15' => ' id-vein-Pattern',
+			'1.3.133.16.840.9.84.1.16' => ' id-thermal-Face-Image',
+			'1.3.133.16.840.9.84.1.17' => ' id-thermal-Hand-Image',
+			'1.3.133.16.840.9.84.1.18' => ' id-lip-Movement',
+			'1.3.133.16.840.9.84.1.19' => ' id-gait',
+			'1.3.133.16.840.9.84.1.2' => ' id-dna',
+			'1.3.133.16.840.9.84.1.3' => ' id-ear-Shape',
+			'1.3.133.16.840.9.84.1.4' => ' id-facial-Features',
+			'1.3.133.16.840.9.84.1.5' => ' id-finger-Image',
+			'1.3.133.16.840.9.84.1.6' => ' id-finger-Geometry',
+			'1.3.133.16.840.9.84.1.7' => ' id-hand-Geometry',
+			'1.3.133.16.840.9.84.1.8' => ' id-iris-Features',
+			'1.3.133.16.840.9.84.1.9' => ' id-keystroke-Dynamics',
+			'1.3.133.16.840.9.84.2' => ' processing-algorithm',
+			'1.3.133.16.840.9.84.3' => ' matching-method',
+			'1.3.133.16.840.9.84.4' => ' format-Owner',
+			'1.3.133.16.840.9.84.4.0' => ' cbeff-Owner',
+			'1.3.133.16.840.9.84.4.1' => ' ibia-Owner',
+			'1.3.133.16.840.9.84.4.1.1' => ' id-ibia-SAFLINK',
+			'1.3.133.16.840.9.84.4.1.10' => ' id-ibia-SecuGen',
+			'1.3.133.16.840.9.84.4.1.11' => ' id-ibia-PreciseBiometric',
+			'1.3.133.16.840.9.84.4.1.12' => ' id-ibia-Identix',
+			'1.3.133.16.840.9.84.4.1.13' => ' id-ibia-DERMALOG',
+			'1.3.133.16.840.9.84.4.1.14' => ' id-ibia-LOGICO',
+			'1.3.133.16.840.9.84.4.1.15' => ' id-ibia-NIST',
+			'1.3.133.16.840.9.84.4.1.16' => ' id-ibia-A3Vision',
+			'1.3.133.16.840.9.84.4.1.17' => ' id-ibia-NEC',
+			'1.3.133.16.840.9.84.4.1.18' => ' id-ibia-STMicroelectronics',
+			'1.3.133.16.840.9.84.4.1.2' => ' id-ibia-Bioscrypt',
+			'1.3.133.16.840.9.84.4.1.3' => ' id-ibia-Visionics',
+			'1.3.133.16.840.9.84.4.1.4' => ' id-ibia-InfineonTechnologiesAG',
+			'1.3.133.16.840.9.84.4.1.5' => ' id-ibia-IridianTechnologies',
+			'1.3.133.16.840.9.84.4.1.6' => ' id-ibia-Veridicom',
+			'1.3.133.16.840.9.84.4.1.7' => ' id-ibia-CyberSIGN',
+			'1.3.133.16.840.9.84.4.1.8' => ' id-ibia-eCryp.',
+			'1.3.133.16.840.9.84.4.1.9' => ' id-ibia-FingerprintCardsAB',
+			'1.3.133.16.840.9.84.4.2' => ' x9-Owner',
+			'1.3.14.2.26.5' => ' sha',
+			'1.3.14.3.2.1.1' => ' rsa',
+			'1.3.14.3.2.10' => ' desMAC',
+			'1.3.14.3.2.11' => ' rsaSignature',
+			'1.3.14.3.2.12' => ' dsa',
+			'1.3.14.3.2.13' => ' dsaWithSHA',
+			'1.3.14.3.2.14' => ' mdc2WithRSASignature',
+			'1.3.14.3.2.15' => ' shaWithRSASignature',
+			'1.3.14.3.2.16' => ' dhWithCommonModulus',
+			'1.3.14.3.2.17' => ' desEDE',
+			'1.3.14.3.2.18' => ' sha',
+			'1.3.14.3.2.19' => ' mdc-2',
+			'1.3.14.3.2.2' => ' md4WitRSA',
+			'1.3.14.3.2.2.1' => ' sqmod-N',
+			'1.3.14.3.2.20' => ' dsaCommon',
+			'1.3.14.3.2.21' => ' dsaCommonWithSHA',
+			'1.3.14.3.2.22' => ' rsaKeyTransport',
+			'1.3.14.3.2.23' => ' keyed-hash-seal',
+			'1.3.14.3.2.24' => ' md2WithRSASignature',
+			'1.3.14.3.2.25' => ' md5WithRSASignature',
+			'1.3.14.3.2.26' => ' sha1',
+			'1.3.14.3.2.27' => ' dsaWithSHA1',
+			'1.3.14.3.2.28' => ' dsaWithCommonSHA1',
+			'1.3.14.3.2.29' => ' sha-1WithRSAEncryption',
+			'1.3.14.3.2.3' => ' md5WithRSA',
+			'1.3.14.3.2.3.1' => ' sqmod-NwithRSA',
+			'1.3.14.3.2.4' => ' md4WithRSAEncryption',
+			'1.3.14.3.2.6' => ' desECB',
+			'1.3.14.3.2.7' => ' desCBC',
+			'1.3.14.3.2.8' => ' desOFB',
+			'1.3.14.3.2.9' => ' desCFB',
+			'1.3.14.3.3.1' => ' simple-strong-auth-mechanism',
+			'1.3.14.7.2.1.1' => ' ElGamal',
+			'1.3.14.7.2.3.1' => ' md2WithRSA',
+			'1.3.14.7.2.3.2' => ' md2WithElGamal',
+			'1.3.36.3' => ' algorithm',
+			'1.3.36.3.1' => ' encryptionAlgorithm',
+			'1.3.36.3.1.1' => ' des',
+			'1.3.36.3.1.1.1.1' => ' desECBPad',
+			'1.3.36.3.1.1.1.1.1' => ' desECBPadISO',
+			'1.3.36.3.1.1.2.1' => ' desCBCPad',
+			'1.3.36.3.1.1.2.1.1' => ' desCBCPadISO',
+			'1.3.36.3.1.2' => ' idea',
+			'1.3.36.3.1.2.1' => ' ideaECB',
+			'1.3.36.3.1.2.1.1' => ' ideaECBPad',
+			'1.3.36.3.1.2.1.1.1' => ' ideaECBPadISO',
+			'1.3.36.3.1.2.2' => ' ideaCBC',
+			'1.3.36.3.1.2.2.1' => ' ideaCBCPad',
+			'1.3.36.3.1.2.2.1.1' => ' ideaCBCPadISO',
+			'1.3.36.3.1.2.3' => ' ideaOFB',
+			'1.3.36.3.1.2.4' => ' ideaCFB',
+			'1.3.36.3.1.3' => ' des-3',
+			'1.3.36.3.1.3.1.1' => ' des-3ECBPad',
+			'1.3.36.3.1.3.1.1.1' => ' des-3ECBPadISO',
+			'1.3.36.3.1.3.2.1' => ' des-3CBCPad',
+			'1.3.36.3.1.3.2.1.1' => ' des-3CBCPadISO',
+			'1.3.36.3.2' => ' hashAlgorithm',
+			'1.3.36.3.2.1' => ' ripemd160',
+			'1.3.36.3.2.2' => ' ripemd128',
+			'1.3.36.3.2.3' => ' ripemd256',
+			'1.3.36.3.2.4' => ' mdc2singleLength',
+			'1.3.36.3.2.5' => ' mdc2doubleLength',
+			'1.3.36.3.3' => ' signatureAlgorithm',
+			'1.3.36.3.3.1' => ' rsa',
+			'1.3.36.3.3.1.1' => ' rsaMitSHA-1',
+			'1.3.36.3.3.1.2' => ' rsaMitRIPEMD160',
+			'1.3.36.3.3.2' => ' ellipticCurve',
+			'1.3.36.3.4' => ' signatureScheme',
+			'1.3.36.3.4.1' => ' iso9796-1',
+			'1.3.36.3.4.2.1' => ' iso9796-2',
+			'1.3.36.3.4.2.2' => ' iso9796-2rsa',
+			'1.3.36.4' => ' attribute',
+			'1.3.36.5' => ' policy',
+			'1.3.36.6' => ' api',
+			'1.3.36.6.1' => ' manufacturerSpecific',
+			'1.3.36.6.2' => ' functionalitySpecific',
+			'1.3.36.7' => ' api',
+			'1.3.36.7.1' => ' keyAgreement',
+			'1.3.36.7.2' => ' keyTransport',
+			'1.3.6.1.4.1.2428.10.1.1' => ' UNINETT policyIdentifier',
+			'1.3.6.1.4.1.2712.10' => ' ICE-TEL policyIdentifier',
+			'1.3.6.1.4.1.3029.32.1' => ' cryptlibEnvelope',
+			'1.3.6.1.4.1.3029.32.2' => ' cryptlibPrivateKey',
+			'1.3.6.1.4.1.311' => ' Microsoft OID',
+			'1.3.6.1.4.1.311.10' => ' Crypto 2.0',
+			'1.3.6.1.4.1.311.10.1' => ' certTrustList',
+			'1.3.6.1.4.1.311.10.1.1' => ' szOID_SORTED_CTL',
+			'1.3.6.1.4.1.311.10.10' => ' Microsoft CMC OIDs',
+			'1.3.6.1.4.1.311.10.10.1' => ' szOID_CMC_ADD_ATTRIBUTES',
+			'1.3.6.1.4.1.311.10.11' => ' Microsoft certificate property OIDs',
+			'1.3.6.1.4.1.311.10.11.1' => ' szOID_CERT_PROP_ID_PREFIX',
+			'1.3.6.1.4.1.311.10.12' => ' CryptUI',
+			'1.3.6.1.4.1.311.10.12.1' => ' szOID_ANY_APPLICATION_POLICY',
+			'1.3.6.1.4.1.311.10.2' => ' nextUpdateLocation',
+			'1.3.6.1.4.1.311.10.3.1' => ' certTrustListSigning',
+			'1.3.6.1.4.1.311.10.3.10' => ' szOID_KP_QUALIFIED_SUBORDINATION',
+			'1.3.6.1.4.1.311.10.3.11' => ' szOID_KP_KEY_RECOVERY',
+			'1.3.6.1.4.1.311.10.3.12' => ' szOID_KP_DOCUMENT_SIGNING',
+			'1.3.6.1.4.1.311.10.3.2' => ' timeStampSigning',
+			'1.3.6.1.4.1.311.10.3.3' => ' serverGatedCrypto',
+			'1.3.6.1.4.1.311.10.3.3.1' => ' szOID_SERIALIZED',
+			'1.3.6.1.4.1.311.10.3.4' => ' encryptedFileSystem',
+			'1.3.6.1.4.1.311.10.3.4.1' => ' szOID_EFS_RECOVERY',
+			'1.3.6.1.4.1.311.10.3.5' => ' szOID_WHQL_CRYPTO',
+			'1.3.6.1.4.1.311.10.3.6' => ' szOID_NT5_CRYPTO',
+			'1.3.6.1.4.1.311.10.3.7' => ' szOID_OEM_WHQL_CRYPTO',
+			'1.3.6.1.4.1.311.10.3.8' => ' szOID_EMBEDDED_NT_CRYPTO',
+			'1.3.6.1.4.1.311.10.3.9' => ' szOID_ROOT_LIST_SIGNER',
+			'1.3.6.1.4.1.311.10.4.1' => ' yesnoTrustAttr',
+			'1.3.6.1.4.1.311.10.5.1' => ' szOID_DRM',
+			'1.3.6.1.4.1.311.10.5.2' => ' szOID_DRM_INDIVIDUALIZATION',
+			'1.3.6.1.4.1.311.10.6.1' => ' szOID_LICENSES',
+			'1.3.6.1.4.1.311.10.6.2' => ' szOID_LICENSE_SERVER',
+			'1.3.6.1.4.1.311.10.7' => ' szOID_MICROSOFT_RDN_PREFIX',
+			'1.3.6.1.4.1.311.10.7.1' => ' szOID_KEYID_RDN',
+			'1.3.6.1.4.1.311.10.8.1' => ' szOID_REMOVE_CERTIFICATE',
+			'1.3.6.1.4.1.311.10.9.1' => ' szOID_CROSS_CERT_DIST_POINTS',
+			'1.3.6.1.4.1.311.12' => ' Catalog',
+			'1.3.6.1.4.1.311.12.1.1' => ' szOID_CATALOG_LIST',
+			'1.3.6.1.4.1.311.12.1.2' => ' szOID_CATALOG_LIST_MEMBER',
+			'1.3.6.1.4.1.311.12.2.1' => ' CAT_NAMEVALUE_OBJID',
+			'1.3.6.1.4.1.311.12.2.2' => ' CAT_MEMBERINFO_OBJID',
+			'1.3.6.1.4.1.311.13' => ' Microsoft PKCS10 OIDs',
+			'1.3.6.1.4.1.311.13.1' => ' szOID_RENEWAL_CERTIFICATE',
+			'1.3.6.1.4.1.311.13.2.1' => ' szOID_ENROLLMENT_NAME_VALUE_PAIR',
+			'1.3.6.1.4.1.311.13.2.2' => ' szOID_ENROLLMENT_CSP_PROVIDER',
+			'1.3.6.1.4.1.311.13.2.3' => ' OS Version',
+			'1.3.6.1.4.1.311.15' => ' Microsoft Java',
+			'1.3.6.1.4.1.311.16' => ' Microsoft Outlook/Exchange',
+			'1.3.6.1.4.1.311.16.4' => ' Outlook Express',
+			'1.3.6.1.4.1.311.17' => ' Microsoft PKCS12 attributes',
+			'1.3.6.1.4.1.311.17.1' => ' szOID_LOCAL_MACHINE_KEYSET',
+			'1.3.6.1.4.1.311.18' => ' Microsoft Hydra',
+			'1.3.6.1.4.1.311.19' => ' Microsoft ISPU Test',
+			'1.3.6.1.4.1.311.2' => ' Authenticode',
+			'1.3.6.1.4.1.311.2.1.10' => ' spcAgencyInfo',
+			'1.3.6.1.4.1.311.2.1.11' => ' spcStatementType',
+			'1.3.6.1.4.1.311.2.1.12' => ' spcSpOpusInfo',
+			'1.3.6.1.4.1.311.2.1.14' => ' certExtensions',
+			'1.3.6.1.4.1.311.2.1.15' => ' spcPelmageData',
+			'1.3.6.1.4.1.311.2.1.18' => ' SPC_RAW_FILE_DATA_OBJID',
+			'1.3.6.1.4.1.311.2.1.19' => ' SPC_STRUCTURED_STORAGE_DATA_OBJID',
+			'1.3.6.1.4.1.311.2.1.20' => ' spcLink',
+			'1.3.6.1.4.1.311.2.1.21' => ' individualCodeSigning',
+			'1.3.6.1.4.1.311.2.1.22' => ' commercialCodeSigning',
+			'1.3.6.1.4.1.311.2.1.25' => ' spcLink',
+			'1.3.6.1.4.1.311.2.1.26' => ' spcMinimalCriteriaInfo',
+			'1.3.6.1.4.1.311.2.1.27' => ' spcFinancialCriteriaInfo',
+			'1.3.6.1.4.1.311.2.1.28' => ' spcLink',
+			'1.3.6.1.4.1.311.2.1.29' => ' SPC_HASH_INFO_OBJID',
+			'1.3.6.1.4.1.311.2.1.30' => ' SPC_SIPINFO_OBJID',
+			'1.3.6.1.4.1.311.2.1.4' => ' spcIndirectDataContext',
+			'1.3.6.1.4.1.311.2.2' => ' CTL for Software Publishers Trusted CAs',
+			'1.3.6.1.4.1.311.2.2.1' => ' szOID_TRUSTED_CODESIGNING_CA_LIST',
+			'1.3.6.1.4.1.311.2.2.2' => ' szOID_TRUSTED_CLIENT_AUTH_CA_LIST',
+			'1.3.6.1.4.1.311.2.2.3' => ' szOID_TRUSTED_SERVER_AUTH_CA_LIST',
+			'1.3.6.1.4.1.311.20' => ' Microsoft Enrollment Infrastructure',
+			'1.3.6.1.4.1.311.20.1' => ' szOID_AUTO_ENROLL_CTL_USAGE',
+			'1.3.6.1.4.1.311.20.2' => ' szOID_ENROLL_CERTTYPE_EXTENSION',
+			'1.3.6.1.4.1.311.20.2.1' => ' szOID_ENROLLMENT_AGENT',
+			'1.3.6.1.4.1.311.20.2.2' => ' szOID_KP_SMARTCARD_LOGON',
+			'1.3.6.1.4.1.311.20.2.3' => ' szOID_NT_PRINCIPAL_NAME',
+			'1.3.6.1.4.1.311.20.3' => ' szOID_CERT_MANIFOLD',
+			'1.3.6.1.4.1.311.21' => ' Microsoft CertSrv Infrastructure',
+			'1.3.6.1.4.1.311.21.1' => ' szOID_CERTSRV_CA_VERSION',
+			'1.3.6.1.4.1.311.21.20' => ' Client Information',
+			'1.3.6.1.4.1.311.25' => ' Microsoft Directory Service',
+			'1.3.6.1.4.1.311.25.1' => ' szOID_NTDS_REPLICATION',
+			'1.3.6.1.4.1.311.3' => ' Time Stamping',
+			'1.3.6.1.4.1.311.3.2.1' => ' SPC_TIME_STAMP_REQUEST_OBJID',
+			'1.3.6.1.4.1.311.30' => ' IIS',
+			'1.3.6.1.4.1.311.31' => ' Windows updates and service packs',
+			'1.3.6.1.4.1.311.31.1' => ' szOID_PRODUCT_UPDATE',
+			'1.3.6.1.4.1.311.4' => ' Permissions',
+			'1.3.6.1.4.1.311.40' => ' Fonts',
+			'1.3.6.1.4.1.311.41' => ' Microsoft Licensing and Registration',
+			'1.3.6.1.4.1.311.42' => ' Microsoft Corporate PKI (ITG)',
+			'1.3.6.1.4.1.311.88' => ' CAPICOM',
+			'1.3.6.1.4.1.311.88.1' => ' szOID_CAPICOM_VERSION',
+			'1.3.6.1.4.1.311.88.2' => ' szOID_CAPICOM_ATTRIBUTE',
+			'1.3.6.1.4.1.311.88.2.1' => ' szOID_CAPICOM_DOCUMENT_NAME',
+			'1.3.6.1.4.1.311.88.2.2' => ' szOID_CAPICOM_DOCUMENT_DESCRIPTION',
+			'1.3.6.1.4.1.311.88.3' => ' szOID_CAPICOM_ENCRYPTED_DATA',
+			'1.3.6.1.4.1.311.88.3.1' => ' szOID_CAPICOM_ENCRYPTED_CONTENT',
+			'1.3.6.1.5.5.7' => ' pkix',
+			'1.3.6.1.5.5.7.1' => ' privateExtension',
+			'1.3.6.1.5.5.7.1.1' => ' authorityInfoAccess',
+			'1.3.6.1.5.5.7.12.2' => ' CMC Data',
+			'1.3.6.1.5.5.7.2' => ' policyQualifierIds',
+			'1.3.6.1.5.5.7.2.1' => ' cps',
+			'1.3.6.1.5.5.7.2.2' => ' unotice',
+			'1.3.6.1.5.5.7.3' => ' keyPurpose',
+			'1.3.6.1.5.5.7.3.1' => ' serverAuth',
+			'1.3.6.1.5.5.7.3.2' => ' clientAuth',
+			'1.3.6.1.5.5.7.3.3' => ' codeSigning',
+			'1.3.6.1.5.5.7.3.4' => ' emailProtection',
+			'1.3.6.1.5.5.7.3.5' => ' ipsecEndSystem',
+			'1.3.6.1.5.5.7.3.6' => ' ipsecTunnel',
+			'1.3.6.1.5.5.7.3.7' => ' ipsecUser',
+			'1.3.6.1.5.5.7.3.8' => ' timeStamping',
+			'1.3.6.1.5.5.7.4' => ' cmpInformationTypes',
+			'1.3.6.1.5.5.7.4.1' => ' caProtEncCert',
+			'1.3.6.1.5.5.7.4.2' => ' signKeyPairTypes',
+			'1.3.6.1.5.5.7.4.3' => ' encKeyPairTypes',
+			'1.3.6.1.5.5.7.4.4' => ' preferredSymmAlg',
+			'1.3.6.1.5.5.7.4.5' => ' caKeyUpdateInfo',
+			'1.3.6.1.5.5.7.4.6' => ' currentCRL',
+			'1.3.6.1.5.5.7.48.1' => ' ocsp',
+			'1.3.6.1.5.5.7.48.2' => ' caIssuers',
+			'1.3.6.1.5.5.8.1.1' => ' HMAC-MD5',
+			'1.3.6.1.5.5.8.1.2' => ' HMAC-SHA',
+			'2.16.840.1.101.2.1.1.1' => ' sdnsSignatureAlgorithm',
+			'2.16.840.1.101.2.1.1.10' => ' mosaicKeyManagementAlgorithm',
+			'2.16.840.1.101.2.1.1.11' => ' sdnsKMandSigAlgorithm',
+			'2.16.840.1.101.2.1.1.12' => ' mosaicKMandSigAlgorithm',
+			'2.16.840.1.101.2.1.1.13' => ' SuiteASignatureAlgorithm',
+			'2.16.840.1.101.2.1.1.14' => ' SuiteAConfidentialityAlgorithm',
+			'2.16.840.1.101.2.1.1.15' => ' SuiteAIntegrityAlgorithm',
+			'2.16.840.1.101.2.1.1.16' => ' SuiteATokenProtectionAlgorithm',
+			'2.16.840.1.101.2.1.1.17' => ' SuiteAKeyManagementAlgorithm',
+			'2.16.840.1.101.2.1.1.18' => ' SuiteAKMandSigAlgorithm',
+			'2.16.840.1.101.2.1.1.19' => ' mosaicUpdatedSigAlgorithm',
+			'2.16.840.1.101.2.1.1.2' => ' mosaicSignatureAlgorithm',
+			'2.16.840.1.101.2.1.1.20' => ' mosaicKMandUpdSigAlgorithms',
+			'2.16.840.1.101.2.1.1.21' => ' mosaicUpdatedIntegAlgorithm',
+			'2.16.840.1.101.2.1.1.22' => ' mosaicKeyEncryptionAlgorithm',
+			'2.16.840.1.101.2.1.1.3' => ' sdnsConfidentialityAlgorithm',
+			'2.16.840.1.101.2.1.1.4' => ' mosaicConfidentialityAlgorithm',
+			'2.16.840.1.101.2.1.1.5' => ' sdnsIntegrityAlgorithm',
+			'2.16.840.1.101.2.1.1.6' => ' mosaicIntegrityAlgorithm',
+			'2.16.840.1.101.2.1.1.7' => ' sdnsTokenProtectionAlgorithm',
+			'2.16.840.1.101.2.1.1.8' => ' mosaicTokenProtectionAlgorithm',
+			'2.16.840.1.101.2.1.1.9' => ' sdnsKeyManagementAlgorithm',
+			'2.16.840.1.113730.1' => ' cert-extension',
+			'2.16.840.1.113730.1.1' => ' netscape-cert-type',
+			'2.16.840.1.113730.1.10' => ' EntityLogo',
+			'2.16.840.1.113730.1.11' => ' UserPicture',
+			'2.16.840.1.113730.1.12' => ' netscape-ssl-server-name',
+			'2.16.840.1.113730.1.13' => ' netscape-comment',
+			'2.16.840.1.113730.1.2' => ' netscape-base-url',
+			'2.16.840.1.113730.1.3' => ' netscape-revocation-url',
+			'2.16.840.1.113730.1.4' => ' netscape-ca-revocation-url',
+			'2.16.840.1.113730.1.7' => ' netscape-cert-renewal-url',
+			'2.16.840.1.113730.1.8' => ' netscape-ca-policy-url',
+			'2.16.840.1.113730.1.9' => ' HomePage-url',
+			'2.16.840.1.113730.2' => ' data-type',
+			'2.16.840.1.113730.2.1' => ' GIF',
+			'2.16.840.1.113730.2.2' => ' JPEG',
+			'2.16.840.1.113730.2.3' => ' URL',
+			'2.16.840.1.113730.2.4' => ' HTML',
+			'2.16.840.1.113730.2.5' => ' netscape-cert-sequence',
+			'2.16.840.1.113730.2.6' => ' netscape-cert-url',
+			'2.16.840.1.113730.3' => ' directory',
+			'2.16.840.1.113730.4.1' => ' serverGatedCrypto',
+			'2.16.840.1.113733.1.6.3' => ' Unknown Verisign extension',
+			'2.16.840.1.113733.1.6.6' => ' Unknown Verisign extension',
+			'2.16.840.1.113733.1.7.1.1' => ' Verisign certificatePolicy',
+			'2.16.840.1.113733.1.7.1.1.1' => ' Unknown Verisign policy qualifier',
+			'2.16.840.1.113733.1.7.1.1.2' => ' Unknown Verisign policy qualifier',
+			'2.23.133' => ' TCPA',
+			'2.23.133.1' => ' tcpa_specVersion',
+			'2.23.133.2' => ' tcpa_attribute',
+			'2.23.133.2.1' => ' tcpa_at_tpmManufacturer',
+			'2.23.133.2.10' => ' tcpa_at_securityQualities',
+			'2.23.133.2.11' => ' tcpa_at_tpmProtectionProfile',
+			'2.23.133.2.12' => ' tcpa_at_tpmSecurityTarget',
+			'2.23.133.2.13' => ' tcpa_at_foundationProtectionProfile',
+			'2.23.133.2.14' => ' tcpa_at_foundationSecurityTarget',
+			'2.23.133.2.15' => ' tcpa_at_tpmIdLabel',
+			'2.23.133.2.2' => ' tcpa_at_tpmModel',
+			'2.23.133.2.3' => ' tcpa_at_tpmVersion',
+			'2.23.133.2.4' => ' tcpa_at_platformManufacturer',
+			'2.23.133.2.5' => ' tcpa_at_platformModel',
+			'2.23.133.2.6' => ' tcpa_at_platformVersion',
+			'2.23.133.2.7' => ' tcpa_at_componentManufacturer',
+			'2.23.133.2.8' => ' tcpa_at_componentModel',
+			'2.23.133.2.9' => ' tcpa_at_componentVersion',
+			'2.23.133.3' => ' tcpa_protocol',
+			'2.23.133.3.1' => ' tcpa_prtt_tpmIdProtocol',
+			'2.23.42.0' => ' contentType',
+			'2.23.42.0.0' => ' PANData',
+			'2.23.42.0.1' => ' PANToken',
+			'2.23.42.0.2' => ' PANOnly',
+			'2.23.42.1' => ' msgExt',
+			'2.23.42.10' => ' national',
+			'2.23.42.10.192' => ' Japan',
+			'2.23.42.2' => ' field',
+			'2.23.42.2.0' => ' fullName',
+			'2.23.42.2.1' => ' givenName',
+			'2.23.42.2.10' => ' amount',
+			'2.23.42.2.2' => ' familyName',
+			'2.23.42.2.3' => ' birthFamilyName',
+			'2.23.42.2.4' => ' placeName',
+			'2.23.42.2.5' => ' identificationNumber',
+			'2.23.42.2.6' => ' month',
+			'2.23.42.2.7' => ' date',
+			'2.23.42.2.7.11' => ' accountNumber',
+			'2.23.42.2.7.12' => ' passPhrase',
+			'2.23.42.2.8' => ' address',
+			'2.23.42.2.9' => ' telephone',
+			'2.23.42.3' => ' attribute',
+			'2.23.42.3.0' => ' cert',
+			'2.23.42.3.0.0' => ' rootKeyThumb',
+			'2.23.42.3.0.1' => ' additionalPolicy',
+			'2.23.42.4' => ' algorithm',
+			'2.23.42.5' => ' policy',
+			'2.23.42.5.0' => ' root',
+			'2.23.42.6' => ' module',
+			'2.23.42.7' => ' certExt',
+			'2.23.42.7.0' => ' hashedRootKey',
+			'2.23.42.7.1' => ' certificateType',
+			'2.23.42.7.2' => ' merchantData',
+			'2.23.42.7.3' => ' cardCertRequired',
+			'2.23.42.7.4' => ' tunneling',
+			'2.23.42.7.5' => ' setExtensions',
+			'2.23.42.7.6' => ' setQualifier',
+			'2.23.42.8' => ' brand',
+			'2.23.42.8.1' => ' IATA-ATA',
+			'2.23.42.8.30' => ' Diners',
+			'2.23.42.8.34' => ' AmericanExpress',
+			'2.23.42.8.4' => ' VISA',
+			'2.23.42.8.5' => ' MasterCard',
+			'2.23.42.8.6011' => ' Novus',
+			'2.23.42.9' => ' vendor',
+			'2.23.42.9.0' => ' GlobeSet',
+			'2.23.42.9.1' => ' IBM',
+			'2.23.42.9.10' => ' Griffin',
+			'2.23.42.9.11' => ' Certicom',
+			'2.23.42.9.12' => ' OSS',
+			'2.23.42.9.13' => ' TenthMountain',
+			'2.23.42.9.14' => ' Antares',
+			'2.23.42.9.15' => ' ECC',
+			'2.23.42.9.16' => ' Maithean',
+			'2.23.42.9.17' => ' Netscape',
+			'2.23.42.9.18' => ' Verisign',
+			'2.23.42.9.19' => ' BlueMoney',
+			'2.23.42.9.2' => ' CyberCash',
+			'2.23.42.9.20' => ' Lacerte',
+			'2.23.42.9.21' => ' Fujitsu',
+			'2.23.42.9.22' => ' eLab',
+			'2.23.42.9.23' => ' Entrust',
+			'2.23.42.9.24' => ' VIAnet',
+			'2.23.42.9.25' => ' III',
+			'2.23.42.9.26' => ' OpenMarket',
+			'2.23.42.9.27' => ' Lexem',
+			'2.23.42.9.28' => ' Intertrader',
+			'2.23.42.9.29' => ' Persimmon',
+			'2.23.42.9.3' => ' Terisa',
+			'2.23.42.9.30' => ' NABLE',
+			'2.23.42.9.31' => ' espace-net',
+			'2.23.42.9.32' => ' Hitachi',
+			'2.23.42.9.33' => ' Microsoft',
+			'2.23.42.9.34' => ' NEC',
+			'2.23.42.9.35' => ' Mitsubishi',
+			'2.23.42.9.36' => ' NCR',
+			'2.23.42.9.37' => ' e-COMM',
+			'2.23.42.9.38' => ' Gemplus',
+			'2.23.42.9.4' => ' RSADSI',
+			'2.23.42.9.5' => ' VeriFone',
+			'2.23.42.9.6' => ' TrinTech',
+			'2.23.42.9.7' => ' BankGate',
+			'2.23.42.9.8' => ' GTE',
+			'2.23.42.9.9' => ' CompuSource',
+			'2.5.29.1' => ' authorityKeyIdentifier',
+			'2.5.29.10' => ' basicConstraints',
+			'2.5.29.11' => ' nameConstraints',
+			'2.5.29.12' => ' policyConstraints',
+			'2.5.29.13' => ' basicConstraints',
+			'2.5.29.14' => ' subjectKeyIdentifier',
+			'2.5.29.15' => ' keyUsage',
+			'2.5.29.16' => ' privateKeyUsagePeriod',
+			'2.5.29.17' => ' subjectAltName',
+			'2.5.29.18' => ' issuerAltName',
+			'2.5.29.19' => ' basicConstraints',
+			'2.5.29.2' => ' keyAttributes',
+			'2.5.29.20' => ' cRLNumber',
+			'2.5.29.21' => ' cRLReason',
+			'2.5.29.22' => ' expirationDate',
+			'2.5.29.23' => ' instructionCode',
+			'2.5.29.24' => ' invalidityDate',
+			'2.5.29.26' => ' issuingDistributionPoint',
+			'2.5.29.27' => ' deltaCRLIndicator',
+			'2.5.29.28' => ' issuingDistributionPoint',
+			'2.5.29.29' => ' certificateIssuer',
+			'2.5.29.3' => ' certificatePolicies',
+			'2.5.29.30' => ' nameConstraints',
+			'2.5.29.31' => ' cRLDistributionPoints',
+			'2.5.29.32' => ' certificatePolicies',
+			'2.5.29.33' => ' policyMappings',
+			'2.5.29.34' => ' policyConstraints',
+			'2.5.29.35' => ' authorityKeyIdentifier',
+			'2.5.29.36' => ' policyConstraints',
+			'2.5.29.37' => ' extKeyUsage',
+			'2.5.29.4' => ' keyUsageRestriction',
+			'2.5.29.5' => ' policyMapping',
+			'2.5.29.6' => ' subtreesConstraint',
+			'2.5.29.7' => ' subjectAltName',
+			'2.5.29.8' => ' issuerAltName',
+			'2.5.29.9' => ' subjectDirectoryAttributes',
+			'2.5.4.0' => ' objectClass',
+			'2.5.4.1' => ' aliasObjectName',
+			'2.5.4.12' => ' title',
+			'2.5.4.13' => ' description',
+			'2.5.4.14' => ' searchGuide',
+			'2.5.4.15' => ' businessCategory',
+			'2.5.4.16' => ' postalAddress',
+			'2.5.4.17' => ' postalCode',
+			'2.5.4.18' => ' postOfficeBox',
+			'2.5.4.19' => ' physicalDeliveryOfficeName',
+			'2.5.4.2' => ' knowledgeInformation',
+			'2.5.4.20' => ' telephoneNumber',
+			'2.5.4.21' => ' telexNumber',
+			'2.5.4.22' => ' teletexTerminalIdentifier',
+			'2.5.4.23' => ' facsimileTelephoneNumber',
+			'2.5.4.24' => ' x121Address',
+			'2.5.4.25' => ' internationalISDNNumber',
+			'2.5.4.26' => ' registeredAddress',
+			'2.5.4.27' => ' destinationIndicator',
+			'2.5.4.28' => ' preferredDeliveryMehtod',
+			'2.5.4.29' => ' presentationAddress',
+			'2.5.4.30' => ' supportedApplicationContext',
+			'2.5.4.31' => ' member',
+			'2.5.4.32' => ' owner',
+			'2.5.4.33' => ' roleOccupant',
+			'2.5.4.34' => ' seeAlso',
+			'2.5.4.35' => ' userPassword',
+			'2.5.4.36' => ' userCertificate',
+			'2.5.4.37' => ' caCertificate',
+			'2.5.4.38' => ' authorityRevocationList',
+			'2.5.4.39' => ' certificateRevocationList',
+			'2.5.4.40' => ' crossCertificatePair',
+			'2.5.4.41' => ' givenName',
+			'2.5.4.42' => ' givenName',
+			'2.5.4.5' => ' serialNumber',
+			'2.5.4.52' => ' supportedAlgorithms',
+			'2.5.4.53' => ' deltaRevocationList',
+			'2.5.4.58' => ' crossCertificatePair',
+			'2.5.4.9' => ' streetAddress',
+			'2.5.8' => ' X.500-Algorithms',
+			'2.5.8.1' => ' X.500-Alg-Encryption',
+			'2.5.8.1.1' => ' rsa',
+			'2.16.76.1.1' => 'DPC',
+			'2.16.76.1.1.0' => 'DPC da AC Raiz',
+			'2.16.76.1.1.1' => 'DPC da AC Presidência',
+			'2.16.76.1.1.2' => 'DPC da AC Serpro',
+			'2.16.76.1.1.3' => 'DPC da SERASA Autoridade Certificadora Principal - ACP',
+			'2.16.76.1.1.4' => 'DPC da SERASA Autoridade Certificadora - AC',
+			'2.16.76.1.1.5' => 'DPC da AC CertiSign na ICP­Brasil',
+			'2.16.76.1.1.6' => 'DPC da AC CertiSign SPB na ICP­Brasil',
+			'2.16.76.1.1.7' => 'DPC da SERASA Certificadora Digital',
+			'2.16.76.1.1.8' => 'DPC da AC SRF',
+			'2.16.76.1.1.9' => 'DPC da AC CAIXA',
+			'2.16.76.1.1.10' => 'DPC da AC CAIXA IN',
+			'2.16.76.1.1.11' => 'DPC da AC CAIXA PJ',
+			'2.16.76.1.1.12' => 'DPC da AC CAIXA PF',
+			'2.16.76.1.1.13' => 'DPC da AC SERPRO SRF',
+			'2.16.76.1.1.14' => 'DPC da Autoridade Certificadora CertiSign Múltipla',
+			'2.16.76.1.1.15' => 'DPC da Autoridade Certificadora CertiSign para Secretaria da Receita Federal',
+			'2.16.76.1.1.16' => 'DPC da AC SERASA SRF',
+			'2.16.76.1.1.17' => 'DPC da Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.1.18' => 'DPC da Autoridade Certificadora PRODEMGE',
+			'2.16.76.1.1.19' => 'DPC da Autoridade Certificadora do Sistema Justiça Federal - AC­JUS',
+			'2.16.76.1.1.20' => 'Declaração   de   Práticas   de   Certificação   da   Autoridade   Certificadora   do SERPRO Final - DPC SERPRO ACF',
+			'2.16.76.1.1' => 'DPC',
+			'2.16.76.1.1.21' => 'Declaração de Práticas de Certificação da Autoridade Certificadora SINCOR',
+			'2.16.76.1.1.22' => 'Declaração de Práticas de Certificação da Autoridade Certificadora Imprensa Oficial SP SRF',
+			'2.16.76.1.1.23' => 'Declaração de Práticas de Certificação da AC FENACOR',
+			'2.16.76.1.1.24' => 'Declaração de Práticas de Certificação da Autoridade Certificadora SERPRO­ JUS',
+			'2.16.76.1.1.25' => 'DPC da AC Caixa Justiça',
+			'2.16.76.1.1.26' => 'DPC da Autoridade Certificadora Imprensa Oficial SP (AC IMESP)',
+			'2.16.76.1.1.27' => 'DPC da Autoridade Certificadora PRODEMGE SRF',
+			'2.16.76.1.1.28' => 'Declaração de Práticas de Certificação da Autoridade Certificadora CertSign  para a Justiça',
+			'2.16.76.1.1.29' => 'DPC da AC SERASA JUS',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.1' => 'A1',
+			'2.16.76.1.2.1.1' => 'Política de Certificados da ACSERPRO para certificados SERPRO­SPB ­ PC SERPRO­SPB',
+			'2.16.76.1.2.1.2' => 'Política de Certificados para certificados da SERASA Autoridade Certificadora',
+			'2.16.76.1.2.1.3' => 'Política   de   Certificados   da   Autoridade   Certificadora   da   Presidência   da República - PCA1',
+			'2.16.76.1.2.1.4' => 'Política   de   Certificado   da   Autoridade   Certificadora   CertiSign   Certificadora Digital para o Sistema de Pagamentos Brasileiro na ICP­Brasil­ PC da AC CertiSign SPB na ICP­Brasil',
+			'2.16.76.1.2.1.5' => 'Política de Certificados SEPROA1',
+			'2.16.76.1.2.1.6' => 'Política de Certificado Digital para Certificado de Assinatura Digital Tipo A1 -  SERASA CD',
+			'2.16.76.1.2.1.7' => 'Política de Certificado de Assinatura Digital do Tipo A1 da AC Caixa IN',
+			'2.16.76.1.2.1.8' => 'Política de Certificado de Assinatura Digital do Tipo A1 da AC Caixa PF',
+			'2.16.76.1.2.1.9' => 'Política de Certificado de Assinatura Digital do Tipo A1 da AC Caixa PJ',
+			'2.16.76.1.2.1.10' => 'Política de Certificados  da  Autoridade  Certificadora  do  Serpro­SRF  para certificados de assinatura digital do tipo A1 (PCSerpro­SRFA1)',
+			'2.16.76.1.2.1.11' => 'Política de  Certificado de  Assinatura Digital do Tipo  A1 da  Autoridade  Certificadora  CertiSign  Múltipla  na  Infra­estrutura  de  Chaves  Públicas  Brasileira',
+			'2.16.76.1.2.1.12' => 'Política  de  Certificado  de   Assinatura   Digital   Tipo   A1   da   Autoridade  Certificadora CertiSign para a Secretaria da Receita Federal',
+			'2.16.76.1.2.1.13' => 'Política de Certificado de Assinatura Digital Tipo A1 da AC SERASA SRF',
+			'2.16.76.1.2.1.14' => 'Política  de Certificado  de  Assinatura   Digital   Tipo  A1  da  Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.2.1.15' => 'Política  de  Certificado  de  Assinatura   Digital  Tipo  A1  da  Autoridade  Certificadora PRODEMGE',
+			'2.16.76.1.2.1.16' => 'Política de Certificados SERPRO do Tipo A1 - PC SERPRO ACF A1',
+			'2.16.76.1.2.1.17' => 'Política de Certificados do SERPRO - SPB - PC SERPRO ACF SPB',
+			'2.16.76.1.2.1.18' => 'Política  de  Certificado  de  Assinatura  Digital  Tipo  A1  da  Autoridade Certificadora SINCOR',
+			'2.16.76.1.2.1.19' => 'Política de Certificado  de  Assinatura  Digital  Tipo  A1  da  Autoridade Certificadora SINCOR para Corretores de Seguros',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.1' => 'A1',
+			'2.16.76.1.2.1.20' => 'Política  de  Certificado  de  Assinatura  Digital  Tipo  A1  da  Autoridade Certificadora Imprensa Oficial SP SRF',
+			'2.16.76.1.2.1.21' => 'Política de Certificados SERPRO­JUS do tipo A1 ­ PCSERPROJUSA1',
+			'2.16.76.1.2.1.22' => 'Política de Certificado de Assinatura Digital do Tipo A1 da AC Caixa Justiça',
+			'2.16.76.1.2.1.23' => 'Política  de Certificado de  Assinatura  Tipo  A1 da  Autoridade  Certificadora PRODEMGE SRF',
+			'2.16.76.1.2.1.24' => 'Política de Certificado de Assinatura Digital Tipo A1 da Autoridade Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.1.25' => 'Política de Certificado Digital da AC  SERASA­JUS para Certificados Tipo A1',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.2' => 'A2',
+			'2.16.76.1.2.2.1' => 'Política de Certificado Digital para Certificado de Assinatura Digital  Tipo A2 - SERASA CD',
+			'2.16.76.1.2.2.2' => 'Política de Certificado Digital para Certificado de Assinatura Digital Tipo A2 da AC SERASA SRF',
+			'2.16.76.1.2.2.3' => 'Política  de  Certificado  de  Assinatura  Digital   do  Tipo  A2  da  Autoridade  Certificadora  CertiSign  últipla  na  Infra­estrutura  de Chaves Públicas Brasileira',
+			'2.16.76.1.2.2.4' => 'Política  de  Certificado  de  Assinatura  Digital  do Tipo  A2  da Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.2.2.5' => 'Política de Certificado de Assinatura Digital do Tipo A2 da AC Caixa Justiça',
+			'2.16.76.1.2.2.6' => 'Política de Certificado de Assinatura Digital Tipo A2 da Autoridade Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.2.7' => 'Política de Certificado  Digital da  AC SERASA­JUS para Certificados Tipo A2',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.3' => 'A3',
+			'2.16.76.1.2.3.1' => 'Política de Certificados da Autoridade Certificadora da Presidência da República - PC ACPR',
+			'2.16.76.1.2.3.2' => 'Política  de  Certificados   da  Autoridade  Certificadora do  SERPRO para certificados SERPRO do tipo A3 - PCSERPROA3',
+			'2.16.76.1.2.3.3' => 'Política de Certificado Digital para Certificado de Assinatura Digital Tipo A3 - SERASA CD',
+			'2.16.76.1.2.3.4' => 'Política de Certificados da Autoridade Certificadora do Serpro­SRF para certificados de assinatura digital do tipo A3 (PCSerpro­SRFA3)',
+			'2.16.76.1.2.3.5' => 'Política de Certificado de Assinatura Digital do Tipo A3 da  Autoridade Certificadora CertiSign Múltipla na Infra­estrutura de Chaves Públicas Brasileira',
+			'2.16.76.1.2.3.6' => 'Política de Certificado de Assinatura Digital Tipo A3 da Autoridade Certificadora CertiSign para a Secretaria da Receita Federal na Infra­estrutura de Chaves Públicas Brasileira',
+			'2.16.76.1.2.3.7' => 'Política de Certificado de Assinatura Digital do Tipo A3 da AC Caixa  IN',
+			'2.16.76.1.2.3.8' => 'Política de Certificado de Assinatura Digital do Tipo A3 da AC Caixa  PF',
+			'2.16.76.1.2.3.9' => 'Política de Certificado de Assinatura Digital do Tipo A3 da AC Caixa   PJ',
+			'2.16.76.1.2.3.10' => 'Política de Certificado de Assinatura Digital do Tipo A3 da AC SERASA SRF',
+			'2.16.76.1.2.3.11' => 'Política de Certificado de Assinatura Digital do Tipo A3 da  Autoridade Certificadora  Imprensa Oficial ­ SP',
+			'2.16.76.1.2.3.12' => 'Política de Certificado de Assinatura Digital do Tipo A3 da  Autoridade Certificadora PRODEMGE',
+			'2.16.76.1.2.3.13' => 'Política de Certificados SERPRO do Tipo A3 - PC SERPRO A3',
+			'2.16.76.1.2.3.14' => 'Política de Certificado de Assinatura Digital Tipo A3 da Autoridade Certificadora SINCOR',
+			'2.16.76.1.2.3.15' => 'Política de Certificado de Assinatura Digital Tipo A3 da Autoridade  Certificadora SINCOR para Corretores de Seguros',
+			'2.16.76.1.2.3.16' => 'Política de Certificado de Assinatura Digital Tipo A3 da Autoridade  Certificadora Imprensa Oficial SP SRF',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.3' => 'A3',
+			'2.16.76.1.2.3.17' => 'Política de Certificado da AC FENACOR A3',
+			'2.16.76.1.2.3.18' => 'Política  de  Certificados  SERPRO­JUS  do  tipo  A3 PCSERPROJUSA3',
+			'2.16.76.1.2.3.19' => 'Política de Certificado de Assinatura Digital do Tipo A3 da AC Caixa Justiça',
+			'2.16.76.1.2.3.20' => 'Política  de  Certificado  de  Assinatura   Tipo   A3   da   Autoridade Certificadora PRODEMGE SRF',
+			'2.16.76.1.2.3.21' => 'Política de Certificado de Assinatura Digital Tipo A3 da Autoridade  Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.3.22' => 'Política   de   Certificado   Digital   da   AC     SERASA­JUS   para Certificados Tipo A3',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.4' => 'A4',
+			'2.16.76.1.2.4.1' => 'Política de Certificado Digital para Certificado de Assinatura Digital  Tipo A4 - SERASA CD;',
+			'2.16.76.1.2.4.2' => 'VAGO',
+			'2.16.76.1.2.4.3' => 'Política de Certificado de Assinatura Digital do Tipo A4 da  Autoridade Certificadora CertiSign Múltipla na Infra­estrutura de Chaves Públicas Brasileira',
+			'2.16.76.1.2.4.4' => 'Política de Certificado de Assinatura Digital Tipo A4 da Autoridade Certificadora CertiSign para a Secretaria da Receita Federal na Infra­estrutura de Chaves Públicas Brasileira',
+			'2.16.76.1.2.4.5' => 'Política de Certificado de Assinatura Digital Tipo A4 da Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.2.4.6' => 'Política de Certificado de Assinatura Digital Tipo A4 da Autoridade Certificadora Imprensa Oficial - SP SRF',
+			'2.16.76.1.2.4.7' => 'Política   de   Certificado   de   Assinatura   Tipo   A4   da   Autoridade Certificadora PRODEMGE SRF',
+			'2.16.76.1.2.4.8' => 'Política de Certificado de Assinatura Digital Tipo A4 da Autoridade Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.4.9' => 'VAGO',
+			'2.16.76.1.2.4.10' => 'Política de Certificado Digital  a  AC SERASA­JUS  para Certificados Tipo A4',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.101' => 'S1',
+			'2.16.76.1.2.101.1' => 'Política de Certificado Digital para Certificado de Sigilo Tipo S1 -  SERASA CD',
+			'2.16.76.1.2.101.2' => 'Política de Certificado de Sigilo Tipo S1 da Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.2.101.3' => 'Política  de  Certificado  de  Sigilo  do  Tipo  S1  da   Autoridade Certificadora CertiSign Múltipla',
+			'2.16.76.1.2.101.4' => 'Política   de   Certificado   de   Sigilo   do   Tipo   S1   da   Autoridade Certificadora PRODEMGE',
+			'2.16.76.1.2.101.5' => 'Política de Certificado de Assinatura Digital do Tipo S1 da AC Caixa Justiça',
+			'2.16.76.1.2.101.6' => 'Política de Certificado de Assinatura Digital Tipo S1 da Autoridade Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.101.7' => 'Política   de   Certificado   Digital   da   AC     SERASA­JUS   para Cerficados Tipo S1',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.102' => 'S2',
+			'2.16.76.1.2.102.1' => 'Política de Certificado Digital para Certificado de Sigilo Tipo S2 - SERASA CD',
+			'2.16.76.1.2.102.2' => 'Política de Certificado de Sigilo Tipo S2 da Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.2.102.3' => 'Política   de   Certificado   de   Sigilo   do   Tipo   S2   da   Autoridade Certificadora CertiSign Múltipla',
+			'2.16.76.1.2.102.4' => 'Política de Certificado de Assinatura Digital do Tipo S2 da AC Caixa Justiça',
+			'2.16.76.1.2.102.5' => 'Política de Certificado de Assinatura Digital Tipo S2 da Autoridade Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.102.6' => 'Política  de  Certificado  Digital  da   AC  SERASA­JUS   para Certificados Tipo S2',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.103' => 'S3',
+			'2.16.76.1.2.103.1' => 'Política de Certificado Digital para Certificado de Sigilo Tipo S3 -  SERASA CD',
+			'2.16.76.1.2.103.2' => 'VAGO',
+			'2.16.76.1.2.103.3' => 'Política   de   Certificado   de   Sigilo   do   Tipo   S3   da   Autoridade Certificadora CertiSign Múltipla',
+			'2.16.76.1.2.103.4' => 'Política de Certificado de Sigilo Tipo S3 da Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.2.103.5' => 'Política de Certificado de Sigilo Tipo S3 da Autoridade Certificadora PRODEMGE',
+			'2.16.76.1.2.103.6' => 'Política de Certificado de Assinatura Digital do Tipo S3 da AC Caixa Justiça',
+			'2.16.76.1.2.103.7' => 'Política de Certificado de Assinatura Digital Tipo S3 da Autoridade Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.103.8' => 'Política   de   Certificado   Digital   da   AC     SERASA­JUS   para Certificados Tipo S3',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.104' => 'S4',
+			'2.16.76.1.2.104.1' => 'Política de Certificado Digital para Certificado de Sigilo Tipo S4 - SERASA CD',
+			'2.16.76.1.2.104.2' => 'VAGO',
+			'2.16.76.1.2.104.3' => 'Política   de   Certificado   de   Sigilo   do   Tipo   S4   da   Autoridade Certificadora CertiSign Múltipla',
+			'2.16.76.1.2.104.4' => 'Política de Certificado de Sigilo Tipo S4 da Autoridade Certificadora Imprensa Oficial ­ SP',
+			'2.16.76.1.2.104.5' => 'Política de Certificado de Assinatura Digital Tipo S4 da Autoridade Certificadora CertiSign para a Justiça',
+			'2.16.76.1.2.104.6' => 'Política   de   Certificado   Digital   da   AC     SERASA­JUS   para Certificados Tipo S4',
+			'2.16.76.1.2' => 'PC',
+			'2.16.76.1.2.201' => 'PC de AC',
+			'2.16.76.1.2.201.1' => 'PC da Serasa Autoridade Certificadora Principal - ACP',
+			'2.16.76.1.2.201.2' => 'PC da AC CertiSign na ICP­Brasil',
+			'2.16.76.1.2.201.3' => 'PC da AC SRF',
+			'2.16.76.1.2.201.4' => 'Política de Certificados da Autoridade Certificadora Caixa',
+			'2.16.76.1.2.201.5' => 'PC da Autoridade Certificadora do Sistema Justiça Federal - AC­ JUS',
+			'2.16.76.1.2.201.6' => 'PC da Autoridade Certificadora do SERPRO (AC SERPRO)',
+			'2.16.76.1.2.201.7' => 'PC da Autoridade Certificadora Imprensa Oficial SP (AC IMESP)',
+			'2.16.76.1.3' => 'Atributos Obrigatórios de Certificados',
+			'2.16.76.1.3.1' => 'campo otherName em certificado de pessoa física',
+			'2.16.76.1.3.2' => 'campo otherName em certificado de pessoa  jurídica',
+			'2.16.76.1.3.3' => 'campo otherName em certificado de pessoa  jurídica',
+			'2.16.76.1.3.4' => 'campo otherName em certificado de pessoa jurídica',
+			'2.16.76.1.3.5' => 'campo otherName em certificado de pessoa física',
+			'2.16.76.1.3.6' => 'campo otherName em certificado de pessoa física',
+			'2.16.76.1.3.7' => 'campo otherName em certificado de pessoa jurídica',
+			'2.16.76.1.4' => 'Atributos Opcionais de  Certificados',
+			'2.16.76.1.4.1' => 'Entidades Sindicais',
+			'2.16.76.1.4.1.1' => 'SINCOR',
+			'2.16.76.1.4.1.1.1' => 'Número de registro do corretor associado');
+
+
+		$result = array();
+		
+		while (strlen($data) > 1) 
+		{
+			$class = ord($data[0]);
+			switch ($class) 
+			{
+				case 0x30:
+					// Sequence
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$sequence_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$values = Crl_parseASN($sequence_data);
+					if (!is_array($values) || is_string($values[0])) 
+					{
+						$values = array($values);
+					}
+					$result[] = array('sequence (' . $len . ')' , $values);
+					break;
+
+				case 0x31:
+					// Set of
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$sequence_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('set (' . $len . ')' , Crl_parseASN($sequence_data));
+					break;
+
+				case 0x01:
+					// Boolean type
+					$boolean_value = (ord($data[2]) == 0xff);
+					$data = substr($data, 3);
+					$result[] = array('boolean (1)' , $boolean_value);
+					break;
+				
+				case 0x02:
+					// Integer type
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$integer_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					if($len == 16)
+					{
+						$result[] = array('integer(' . $len . ')', $integer_data);
+						break;
+					}
+					else
+					{
+						$value = 0;
+						if ($len <= 4) 
+						{
+							// Method works fine for small integers
+							for ($i = 0; $i < strlen($integer_data); $i++) 
+							{
+								$value = ($value << 8) | ord($integer_data[$i]);
+							}
+						} 
+						else 
+						{
+							// Method works for arbitrary length integers 
+							if (extension_loaded('bcmath')) 
+							{
+								for ($i = 0; $i < strlen($integer_data); $i++) 
+								{
+									$value = bcadd(bcmul($value, 256), ord($integer_data[$i]));
+								}
+							} 
+							else 
+							{
+								$value = -1;
+							}
+						}
+						$result[] = array('integer(' . $len . ')', $value);
+						break;
+					}
+
+				case 0x03:
+					// Bitstring type
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$bitstring_data = substr($data, 2+bytes ,  $len);
+					$data = substr($data, 2 + $bytes + $len);
+					//$result[] = array('bit string (' . $len . ')' ,Crl_parseASN($bitstring_data));
+					$result[] = array('bit string (' . $len . ')' ,'UnsedBits:'.ord($bitstring_data[0]).':'.ord($bitstring_data[1]));
+					break;
+
+				case 0x04:
+					// Octetstring type 
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$octectstring_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					if($context_especific)
+					{
+						$result[] = array('octet string(' . $len . ')'  , $octectstring_data);
+					}
+					else
+					{
+						$result[] = array('octet string (' . $len . ')' , Crl_parseASN($octectstring_data));
+					}
+					break;
+					
+				case 0x0C:
+					// UTF8 STRING
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$octectstring_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					if($context_especific)
+					{
+						$result[] = array('utf8 string(' . $len . ')'  , $octectstring_data);
+					}
+					else
+					{
+						$result[] = array('utf8 string (' . $len . ')' , Crl_parseASN($octectstring_data));
+					}
+					break;
+
+				case 0x05:
+					// Null type
+					$data = substr($data, 2);
+					$result[] = array('null', null);
+					break;
+
+				case 0x06:
+					// Object identifier type
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$oid_data = substr($data, 2 + $bytes, $len);
+					$x_len = $data[1];
+					$data = substr($data, 2 + $bytes + $len);
+
+					// Unpack the OID
+					$plain  = floor(ord($oid_data[0]) / 40);
+					$plain .= '.' . ord($oid_data[0]) % 40;
+
+					$value = 0;
+					$i = 1;
+					while ($i < strlen($oid_data)) 
+					{
+						$value = $value << 7;
+						$value = $value | (ord($oid_data[$i]) & 0x7f);
+
+						if (!(ord($oid_data[$i]) & 0x80)) 
+						{
+							$plain .= '.' . $value;
+							$value = 0;
+						}
+						$i++;
+					}
+
+					if (isset($_oids[$plain])) 
+					{
+						$result[] =  array('oid(' . $len . '): '  . $plain, $_oids[$plain]);
+					} 
+					else 
+					{
+						$result[] = array('oid(' . $len . '): '  . $plain, $plain);
+					}
+					break;
+
+				case 0x16:
+					// Character string type
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$string_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('IA5 String (' . $len . ')'  , $string_data);
+					break;
+					
+				case 0x12:
+				case 0x14:
+				case 0x15:	
+				case 0x81:			
+					// Character string type
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$string_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('string (' . $len . ')'  , $string_data);
+					break;
+
+				case 0x80:
+					// Character string type
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$string_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('string (' . $len . ')'  , print_hex($string_data));
+					break;
+					
+				case 0x13:
+				case 0x86:
+					// Printable string type
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$string_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('Printable String (' . $len . ')'  , $string_data);
+					break;
+
+				case 0x17:
+					// Time types
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$time_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('utctime (' . $len . ')'  , $time_data);
+					break;
+
+				case 0x82:
+					// X509v3 extensions?
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$sequence_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('extension : X509v3 extensions (' . $len . ')'  , array(Crl_parseASN($sequence_data)));
+					//$result[] = Crl_parseASN($sequence_data);
+					break;
+
+				case 0xa0:
+					// Extensions
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$extension_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('Context Especific (' . $len . ')' , array(Crl_parseASN($extension_data,true)));
+					break;
+					
+				case 0xa3:
+					// Extensions
+					$len = ord($data[1]);
+					$bytes = 0;
+					get_length(&$len,&$bytes,$data);
+					$extension_data = substr($data, 2 + $bytes, $len);
+					$data = substr($data, 2 + $bytes + $len);
+					$result[] = array('extension (0xA3)  (' . $len . ')' ,array(Crl_parseASN($extension_data)));
+					break;
+
+				case 0xe6:
+					$extension_data = substr($data, 0, 1);
+					$data = substr($data, 1);
+					$result[] = array('extension (0xE6) (' . $len . ')'  , dechex($extension_data));
+					break;
+
+				case 0xa1:
+					$extension_data = substr($data, 0, 1);
+					$data = substr($data, 6);
+					$result[] = array('extension (0xA1) (' . $len . ')'  , dechex($extension_data));
+					break;
+
+				default:
+					// Unknown
+					$result[] = 'UNKNOWN' .  $data;;
+					//file_put_contents('/opt/lampp/htdocs/security/temp/arquivo_desconhecido.txt',$data);
+					$data = '';
+					break;
+			}
+        }
+
+        return (count($result) > 1) ? $result : array_pop($result);
+
+   }
+   
+   function openssl_to_timestamp ($in) 
+   {
+	$year  = substr($in, 0, 4); /* NOTE: Yes, this returns a two digit year */
+        $month = substr($in, 4, 2);
+        $day   = substr($in, 6, 2);
+        $hour  = substr($in, 8, 2);
+        $min   = substr($in, 10, 2);
+        $sec   = substr($in, 12, 2);
+    
+        return gmmktime($hour, $min, $sec, $month, $day, $year);
+	}
+
+#============================================================================================
+# Transforma o certificado do formato PEM para o formato DER ...
+function pem2der($pem_data) 
+	{
+		$begin = "CERTIFICATE-----";
+		$end   = "-----END";
+		$pem_data = substr($pem_data, strpos($pem_data, $begin)+strlen($begin));    
+		$pem_data = substr($pem_data, 0, strpos($pem_data, $end));
+		$der = base64_decode($pem_data);
+		return $der;
+	}
+
+
+function recupera_dados_oid($certificado_digital_formato_der, $oid)
+	{
+		// Esta função assume que a oid esta inserida dentro de uma estrutura do tipo "sequencia" , como primeiro elemento da estrutura...
+	        $oid_hexa = OIDtoHex($oid);                                                        // converte oid de texto para hexadecimal ...
+		$partes = explode($oid_hexa,$certificado_digital_formato_der);    // Faz o split pela oid...
+		$ret = array();
+		if(count($partes)>1)
+			{
+				for($i=1;$i<count($partes);$i++)
+					{
+					        //O inicio da seq pode estar a 3 ou 2 digitos antes do inicio da oid .... depende do numero de bytes usados para  tamanho da seq.
+						$xcv4 = substr($partes[$i-1],strlen($partes[$i-1])-4,4); // recupera da primeira parte os 4 ultimos digitos...
+						$xcv3 = substr($partes[$i-1],strlen($partes[$i-1])-3,3); // recupera da primeira parte os 3 ultimos digitos...
+						$xcv2 = substr($partes[$i-1],strlen($partes[$i-1])-2,2); // recupera da primeira parte os 2 ultimos digitos...
+						if($xcv4[0] == chr(0x30))
+							{
+								$xcv = $xcv4;
+							}
+						else
+							{
+								if($xcv3[0] == chr(0x30))
+									{
+										$xcv = $xcv3;
+									}
+								else
+									{
+										$xcv = $xcv2;
+									}
+							}
+						$data = $xcv . $oid_hexa . $partes[$i];           // reconstroi a sequencia.....				
+						$len = ord($data[1]);
+						$bytes = 0;
+						get_length(&$len,&$bytes,$data);                  // obtem tamanho da parte de dados da oid.
+						$oid_data = substr($data,2 + $bytes,$len);    // Obtem porcao de bytes pertencentes a oid.
+						$ret[] =  Crl_parseASN($oid_data);                 // parse dos dados da oid.
+					} 
+			}
+		return $ret;
+	}
+
+
+# Recupera dados da oid passada como parametro.....
+function parse($oid,$valor)
+	{
+		//
+		//  OID's PESSOA FISICA = 2.16.76.1.3.1 ,  2.16.76.1.3.6 ,  2.16.76.1.3.5 ,  2.16.76.1.4.n ... as 2.16.1.4.n não são obrigatórias e ão sao tratadas..
+		//
+		//  OID's PESSOA JURIDICA = 2.16.76.1.3.4 ,  2.16.76.1.3.2 , 2.16.76.1.3.3 , 2.16.76.1.3.7 
+		//
+		//  OID's EQUIPAMENTO/APLICAÇÃO = 2.16.76.1.3.8 ,  2.16.76.1.3.3 , 2.16.76.1.3.2 , 2.16.76.1.3.4 
+		//	
+		//  OID  para logon no NT:  1.3.6.1.4.1.311.20.2.3
+		//
+		$oids = array('2.16.76.1.3.1' => array('1'=>array('NASCIMENTO',8),
+		                                               '2'=>array('CPF',11),
+							       '3'=>array('NIS',11),
+							       '4'=>array('RG',15),
+							       '5'=>array('ORGAOUF',6)),
+				     '2.16.76.1.3.2' => array('1'=>array('NOMERESPONSAVELCERTIFICADO',0)),
+				     '2.16.76.1.3.3' => array('1'=>array('CNPJ',14)),
+			             '2.16.76.1.3.4' => array('1'=>array('NASCIMENTO',8),
+		                                               '2'=>array('CPF',11),
+							       '3'=>array('NIS',11),
+							       '4'=>array('RG',15),
+							       '5'=>array('ORGAOUF',6)),	
+				     '2.16.76.1.3.5' => array('1'=>array('TITULO',12),
+				                              '2'=>array('ZONA',3),
+							      '3'=>array('SECAO',4),
+							      '4'=>array('TITULO_CIDADE_UF',0)),	
+				     '2.16.76.1.3.6' => array('1'=>array('CADINSS',12)),							      
+				     '2.16.76.1.3.7' => array('1'=>array('CEI',12)),								       
+				     '2.16.76.1.3.8' => array('1'=>array('NOMEEMPRESARIAL',0)),
+				     '1.3.6.1.4.1.311.20.2.3' => array('1'=>array('NTNOMEPRINCIPAL',0)));				     
+				     
+		$resultado = array(); 
+		$esta_oid = $oids[$oid];
+		$p = 0;
+		for($i=1;$i < count($esta_oid) + 1; $i++)
+			{
+				if($esta_oid[$i][1] == 0)
+					{
+						# se igual a zero, então esta apontando um ultimo elemento, iniciando em $p, até o fim dos dados
+						$tamanho = strlen($valor) - $p;
+					}
+				else
+					{
+						$tamanho = $esta_oid[$i][1];
+					} 
+				$resultado[$oid][$esta_oid[$i][0]] = substr($valor,$p,$tamanho);
+				// A linha logo abaixo he para manter compatibilidade com versoes anteriores... Sera desativada assim que possivel...
+				$resultado[$esta_oid[$i][0]] = substr($valor,$p,$tamanho);
+				$p = $p + $esta_oid[$i][1];
+			}
+
+		return $resultado;
+	}  
+
+	
+function subjectAltName($xx, $certificado_digital_formato_der)
+	{
+		$dados = array();
+		$ret = recupera_dados_oid($certificado_digital_formato_der,'2.5.29.17');
+		if(count($ret))
+		{
+			if(substr($ret[0][1][0],0,12) == 'octet string')
+				{
+					$ret = $ret[0][1][1][1];
+				}
+			else
+				{
+					$ret = $ret[0][2][1][1];  // Se não iniciou por um octet string skipa para o próximo item na estrutura.
+				}
+			foreach($ret as $group)
+				{
+					if(substr($group[0],0,17) == 'Context Especific')  // primeiro indice tem de ter o valor  'Context Especific' ...
+						{
+							$oid = explode(':',$group[1][0][0][0]);   //  Pega o numero da oid.
+							$dados = array_merge(parse(trim($oid[1]), $group[1][0][1][1][0][1]),$dados); // Passa a oid e o seu valor para ser parseado....
+						}
+					if(substr($group[0],0,6) == 'string')
+						{
+							if(strpos($group[1],'@'))                  //se he email tem de ter uma @.
+								{
+									$aux_email = $group[1];
+								}
+						}
+				}
+			// O  EMAIL foi localizado no loop de tratamento das OIDs.....
+			$dados['EMAIL'] = $aux_email;
+		}
+		return $dados;
+	}
+
+	
+function CRLDistributionPointsxx($xx, $certificado_digital_formato_der)
+	{
+		$AUX = recupera_dados_oid($certificado_digital_formato_der,'2.5.29.31');
+		$i=1;
+		if(substr($AUX[0][1][0],0,7) == 'boolean')
+			{
+				$i=2;
+			}
+		$AUX1 = $AUX[0][$i][1][1][0];
+		// Pode existir mais de uma crl, mas vamos considerar apenas a primeira.....
+		return array('CRLDISTRIBUTIONPOINTS' => $AUX1[1][0][1][0][1][0][1]);		
+	}	
+
+function CRLDistributionPoints($xx, $certificado_digital_formato_der)
+	{
+		$AUX = recupera_dados_oid($certificado_digital_formato_der,'2.5.29.31');
+		$i=1;
+		if(substr($AUX[0][1][0],0,7) == 'boolean')
+			{
+				$i=2;
+			}
+                $ret = array();
+		if($AUX[0][$i][1][1])
+			{
+				if(is_array($AUX[0][$i][1][1]))
+					{
+						//Pode existir mais de um local para obter a CRL.
+						foreach($AUX[0][$i][1][1] as $crl)
+						  {
+						      if(substr($crl[1][0][1][0][1][0][1],0,4) == 'http')
+							{
+								$ret[] = $crl[1][0][1][0][1][0][1];
+							}
+						  }
+					  }
+			  }
+                return array('CRLDISTRIBUTIONPOINTS' => $ret);
+	}	
+
+
+function SERIALNUMBER($cert_data,$KK)
+	{
+		$dados = array();
+		if($cert_data[1][0][1][$KK][1])
+			{
+				$dados['SERIALNUMBER'] = $cert_data[1][0][1][$KK][1];
+			}
+		return $dados;			
+	}
+	
+function SUBJECT($cert_data,$KK)
+	{
+		$dados = array();
+		$dados['SUBJECT'] = array();
+		foreach($cert_data[1][0][1][$KK][1] as $AUX2)
+			{
+				$dados['SUBJECT'][$AUX2[1][1][0][1]] = $AUX2[1][1][1][1];
+			}
+			$AUX = explode(':',$dados['SUBJECT']['CN']);
+		$dados['NOME'] = $AUX[0];
+		return $dados;			
+	}
+		
+function ISSUER($cert_data,$KK)		
+	{
+		$dados = array();	
+		$AUX0 = '/';
+		$dados['EMISSOR_CAMINHO_COMPLETO']  = array();
+		
+		foreach($cert_data[1][0][1][$KK][1] as $AUX2)
+			{
+				$dados['EMISSOR_CAMINHO_COMPLETO'][$AUX2[1][1][0][1]] = $AUX2[1][1][1][1] ;
+			}
+		$dados['EMISSOR'] = $dados['EMISSOR_CAMINHO_COMPLETO']['CN'];
+		return $dados;	
+	}		
+
+
+function BEFOREAFTER($cert_data,$KK)
+	{
+		$dados = array();
+		$dados['INICIO_VALIDADE'] = data_hora($cert_data[1][0][1][$KK][1][0][1]);
+		$dados['FIM_VALIDADE'] = data_hora($cert_data[1][0][1][$KK][1][1][1]);
+		$agora = date('YmdHis');
+		if(($agora < $dados['INICIO_VALIDADE']) || ($agora > $dados['FIM_VALIDADE']))
+			{
+				$dados['EXPIRADO'] = true;
+			}
+		else
+			{
+				$dados['EXPIRADO'] = false;
+			}	
+		return $dados;		
+		}	
+
+
+function AUTHORITYKEYIDENTIFIER($xx, $certificado_digital_formato_der)
+	{
+		$dados = array();
+		if (isset($certificado_digital_formato_der))
+			{
+				$caid = recupera_dados_oid($certificado_digital_formato_der,'2.5.29.35');
+				$i=1;
+				if(substr($caid[0][1][0],0,7) == 'boolean')
+					{
+						$i=2;
+					}
+				$dados['AUTHORITYKEYIDENTIFIER'] = $caid[0][$i][1][1][0][1];
+			}
+		else
+			{
+				// Se nao existir um valor, assume certificado auto assinado .....
+				$dados['AUTHORITYKEYIDENTIFIER'] = "auto-assinado";
+			}
+		return $dados;			
+	}
+	
+function KEYUSAGE($xx, $certificado_digital_formato_der)
+	{
+		$KeyUsage= array( 0x80 => 'digitalSignature', 
+			      0x40 => 'nonRepudiation',
+                              0x20 => 'keyEncipherment',
+			      0x10 => 'dataEncipherment',
+			      0x08 => 'keyAgreement',
+			      0x04 => 'keyCertSign',
+			      0x02 => 'cRLSign');
+
+		$dados = array();
+		if (isset($certificado_digital_formato_der))
+			{
+				$AUX = recupera_dados_oid($certificado_digital_formato_der,'2.5.29.15');  // busca oid do keyusage
+				$AUX = explode(':',$AUX[0][2][1][1]);
+				if(count($AUX) == 3)
+					{
+						foreach($KeyUsage as $chave => $valor)
+							{
+								if($AUX[2] & $chave)
+									{
+										$dados['KEYUSAGE'][$valor] = TRUE;
+									}
+							}
+					}
+			}	
+		return $dados;			
+	}
+
+function EXTKEYUSAGE($xx, $certificado_digital_formato_der)
+	{
+		$dados = array();
+		if (isset($certificado_digital_formato_der))
+			{
+				$AUX = recupera_dados_oid($certificado_digital_formato_der,'2.5.29.37');  // busca oid do extkeyusage
+				$AUX1 = $AUX[0][count($AUX[0])-1];
+				if(count($AUX1) > 0)
+				{
+				foreach($AUX1[1][1] as $itens)
+					{
+					        $AUX2 = explode(':',$itens[0]);
+						$dados['EXTKEYUSAGE'][trim($itens[1])] =  trim($AUX2[1]);
+					}
+				}
+			}
+		return $dados;			
+	}
+	
+function BASICCONSTRAINTS($xx, $certificado_digital_formato_der)
+	{
+		$dados = array();
+		if (isset($certificado_digital_formato_der))
+			{
+				$AUX = recupera_dados_oid($certificado_digital_formato_der,'2.5.29.19');  // busca oid do BasicConstraints
+				if(count($AUX) > 0)
+					{
+						$dados['CA'] = $AUX[0][count($AUX[0])-1][1][1][0][1];
+					}
+			}
+		return $dados;			
+	}
+	
+function recupera_dados_do_ceritificado_digital($certificado_digital_formato_pem)
+	{
+		$cert_der =  pem2der($certificado_digital_formato_pem);
+		
+		$funcoes = array(SERIALNUMBER => 1,
+					ISSUER => 3,
+					BEFOREAFTER => 4,
+					SUBJECT => 5,
+					AUTHORITYKEYIDENTIFIER => $cert_der,
+					KEYUSAGE => $cert_der,
+					EXTKEYUSAGE => $cert_der,
+					BASICCONSTRAINTS => $cert_der,
+					CRLDistributionPoints =>  $cert_der,
+					subjectAltName => $cert_der);
+			   
+		$dados=array();
+		
+		$cert_data = Crl_parseASN( $cert_der);
+		
+		foreach($funcoes as $funcao => $parametro)
+			{
+				$dados= array_merge($dados,$funcao($cert_data,$parametro));
+			}
+
+		if(!$dados['EMAIL'])
+			{
+				$AUX = explode('emailAddress=',$dados['SUBJECT']);
+				if(count($AUX) > 1)
+					{
+						$dados['EMAIL'] = $AUX[1];
+					}
+			}
+		return $dados;		
+	}
+
+#============================================================================================
+
+?>
