Popular Post cjulio Posted March 18, 2026 at 12:23 PM Popular Post Report #635644 Posted March 18, 2026 at 12:23 PM (edited) Estava farto de andar com comandos OpenSSL cada vez que precisava de renovar o certificado para os webservices da AT. Fiz esta pequena aplicação que trata de tudo, gera a chave privada, o CSR, importa o certificado recebido e exporta o PFX e o CER prontos a usar. Quem quiser usar, está à vontade, e melhorar 🙂 Só fiquei lixado (fudi...) 😠 porque não existe um sitio fixo na AT para ir buscar a ChaveCifraPublicaAT sem password e sem 2027, teria que estar sempre a alterar o link... using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Text; using System.Windows.Forms; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Security; using Org.BouncyCastle.X509; namespace GeradorCertificadosSSL { static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FormPrincipal()); } } // ========================================================= // FORMULÁRIO PRINCIPAL // ========================================================= public class FormPrincipal : Form { // Constantes private ListBox lstFicheiros; private const string NUMERO_CONTRIBUINTE = "-----------"; private const string PAIS = "--"; private const string DISTRITO = "-------"; private const string CONCELHO = "-----"; private const string ORGANIZACAO = "----"; private const string DEPARTAMENTO = "-------"; private const string EMAIL = "------"; private const string PASSWORD_DEFAULT = "-----"; private static readonly string ANO = DateTime.Now.Year.ToString(); private static readonly string DIRETORIA_BASE = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Certificados", ANO); private const string NOMEFINAL = "-----"; // Controlos private TabControl tabControl; private TabPage tabCSR, tabImportar, tabExportar; private RichTextBox txtLog; private StatusStrip statusStrip; private ToolStripStatusLabel lblStatus; public FormPrincipal() { Directory.CreateDirectory(DIRETORIA_BASE); InitializeComponent(); //var x = DownloadCertificadoServidor(); } // ------------------------------------------------------- // INICIALIZAÇÃO DO FORMULÁRIO // ------------------------------------------------------- private void InitializeComponent() { Text = "Gerador de Certificados SSL — E-Fatura"; Size = new Size(820, 680); MinimumSize = new Size(820, 680); StartPosition = FormStartPosition.CenterScreen; Font = new Font("Segoe UI", 9.5f); BackColor = Color.FromArgb(245, 246, 250); // ---- TAB CONTROL ---- tabControl = new TabControl { Dock = DockStyle.Fill, Padding = new Point(12, 6) }; tabCSR = new TabPage("1 — Gerar CSR"); tabImportar = new TabPage("2 — Importar CRT"); tabExportar = new TabPage("3 — Exportar Ficheiros"); tabControl.TabPages.AddRange(new[] { tabCSR, tabImportar, tabExportar }); // ---- LOG ---- var panelLog = new Panel { Dock = DockStyle.Bottom, Height = 180, Padding = new Padding(8) }; var lblLog = new Label { Text = "Registo de operações:", Dock = DockStyle.Top, Height = 20, ForeColor = Color.FromArgb(90, 90, 110) }; txtLog = new RichTextBox { Dock = DockStyle.Fill, ReadOnly = true, BackColor = Color.FromArgb(30, 30, 40), ForeColor = Color.FromArgb(180, 255, 180), Font = new Font("Consolas", 9f), BorderStyle = BorderStyle.None }; panelLog.Controls.AddRange(new Control[] { txtLog, lblLog }); // ---- STATUS ---- statusStrip = new StatusStrip(); lblStatus = new ToolStripStatusLabel($"Directoria: {DIRETORIA_BASE}") { Spring = true, TextAlign = ContentAlignment.MiddleLeft }; statusStrip.Items.Add(lblStatus); // ---- SEPARADOR ---- var splitter = new Splitter { Dock = DockStyle.Bottom, Height = 4 }; Controls.AddRange(new Control[] { tabControl, splitter, panelLog, statusStrip }); // ---- PÁGINAS ---- BuildTabCSR(); BuildTabImportar(); BuildTabExportar(); Log($"Aplicação iniciada. Directoria de trabalho: {DIRETORIA_BASE}"); } // ------------------------------------------------------- // TAB 1 — GERAR CSR // ------------------------------------------------------- private void BuildTabCSR() { var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) }; // Campos editáveis var fields = new (string Label, string Value)[] { ("NIF", NUMERO_CONTRIBUINTE), ("País", PAIS), ("Distrito", DISTRITO), ("Concelho", CONCELHO), ("Organização", ORGANIZACAO), ("Departamento", DEPARTAMENTO), ("Email", EMAIL), }; var textBoxes = new TextBox[fields.Length]; int y = 20; int yDireita = y; // guardar o y inicial para a coluna da direita var lblTitulo = new Label { Text = "Dados do Certificado", Font = new Font("Segoe UI", 11f, FontStyle.Bold), ForeColor = Color.FromArgb(50, 50, 120), Location = new Point(20, y), AutoSize = true }; panel.Controls.Add(lblTitulo); y += 35; for (int i = 0; i < fields.Length; i++) { var lbl = new Label { Text = fields[i].Label + ":", Location = new Point(20, y + 3), Size = new Size(110, 22), ForeColor = Color.FromArgb(60, 60, 80) }; var txt = new TextBox { Text = fields[i].Value, Location = new Point(140, y), Size = new Size(200, 26), Enabled = false, Name = "txt_" + fields[i].Label }; textBoxes[i] = txt; panel.Controls.Add(lbl); panel.Controls.Add(txt); y += 36; } int xDireita = 370; int larguraDireita = 400; var lblCsrTitulo = new Label { Text = "CSR gerado:", Location = new Point(xDireita, yDireita), Font = new Font("Segoe UI", 11f, FontStyle.Bold), ForeColor = Color.FromArgb(50, 50, 120), AutoSize = true }; panel.Controls.Add(lblCsrTitulo); yDireita += 35; var txtCsr = new TextBox { Multiline = true, ScrollBars = ScrollBars.Vertical, ReadOnly = true, Location = new Point(xDireita, yDireita), Size = new Size(larguraDireita, fields.Length * 36 - 50), // mesma altura que os campos Font = new Font("Consolas", 7f), BackColor = Color.FromArgb(250, 250, 255) }; panel.Controls.Add(txtCsr); var btnCopiar = new Button { Text = "📋 Copiar CSR", Location = new Point(xDireita, fields.Length * 36 + 20), Size = new Size(140, 30), BackColor = Color.FromArgb(80, 160, 80), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, Enabled = false, Cursor = Cursors.Hand }; btnCopiar.FlatAppearance.BorderSize = 0; panel.Controls.Add(btnCopiar); y += 35; // Botão gerar var btnGerar = new Button { Text = "▶ Gerar Chave Privada + CSR", Location = new Point(140, y), Size = new Size(260, 38), BackColor = Color.FromArgb(50, 100, 200), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, Font = new Font("Segoe UI", 10f, FontStyle.Bold), Cursor = Cursors.Hand }; btnGerar.FlatAppearance.BorderSize = 0; panel.Controls.Add(btnGerar); y += 52; string csrExistente = Path.Combine(DIRETORIA_BASE, $"{NUMERO_CONTRIBUINTE}.csr"); if (File.Exists(csrExistente)) { txtCsr.Text = File.ReadAllText(csrExistente); btnCopiar.Enabled = true; btnGerar.Text = "🔄 Regenerar Chave Privada + CSR"; btnGerar.BackColor = Color.FromArgb(180, 100, 30); Log($"CSR existente carregado: {csrExistente}"); } btnGerar.Click += (s, e) => { // Avisar se já existe string csrPath = Path.Combine(DIRETORIA_BASE, $"{NUMERO_CONTRIBUINTE}.csr"); if (File.Exists(csrPath)) { var resposta = MessageBox.Show( "Já existe um CSR gerado.\n\nSe regenerar será criada uma nova chave privada e o certificado anterior ficará inválido.\n\nTem a certeza?", "Atenção", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (resposta != DialogResult.Yes) return; } GerarCSR( textBoxes[0].Text, textBoxes[1].Text, textBoxes[2].Text, textBoxes[3].Text, textBoxes[4].Text, textBoxes[5].Text, textBoxes[6].Text, out string csrPem); if (!string.IsNullOrEmpty(csrPem)) { txtCsr.Text = csrPem; btnCopiar.Enabled = true; btnGerar.Text = "🔄 Regenerar Chave Privada + CSR"; btnGerar.BackColor = Color.FromArgb(180, 100, 30); } }; btnCopiar.Click += (s, e) => { Clipboard.SetText(txtCsr.Text); Log("CSR copiado para a área de transferência."); MessageBox.Show("CSR copiado!\nCole-o no portal da AT.", "Copiado", MessageBoxButtons.OK, MessageBoxIcon.Information); }; tabCSR.Controls.Add(panel); } // ------------------------------------------------------- // TAB 2 — IMPORTAR CRT + CHAVE PÚBLICA AT // ------------------------------------------------------- private void BuildTabImportar() { var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) }; int y = 20; AddSectionLabel(panel, "Passo 1 — Certificado recebido da AT (.crt)", ref y); // Larguras e posições das duas colunas int xEsquerda = 20; int xDireita = 390; int largura = 360; int alturaTexto = 160; // --- Coluna esquerda: CRT --- var lblCrt = new Label { Text = "Cole o conteúdo do .crt:", Location = new Point(xEsquerda, y), AutoSize = true, ForeColor = Color.FromArgb(60, 60, 80) }; panel.Controls.Add(lblCrt); var txtCrt = new TextBox { Multiline = true, ScrollBars = ScrollBars.Vertical, Location = new Point(xEsquerda, y + 22), Size = new Size(largura, alturaTexto), Font = new Font("Consolas", 7f), BackColor = Color.FromArgb(250, 250, 255) }; panel.Controls.Add(txtCrt); var btnEscolherCrt = new Button { Text = "📂 Escolher ficheiro .crt / .cer", Location = new Point(xEsquerda, y + 22 + alturaTexto + 6), Size = new Size(220, 30), BackColor = Color.FromArgb(100, 130, 180), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, Cursor = Cursors.Hand }; btnEscolherCrt.FlatAppearance.BorderSize = 0; panel.Controls.Add(btnEscolherCrt); // --- Coluna direita: Chave Pública AT --- var lblChave = new Label { Text = "Cole o conteúdo da ChavePublicaAT.cer:", Location = new Point(xDireita, y), AutoSize = true, ForeColor = Color.FromArgb(60, 60, 80) }; panel.Controls.Add(lblChave); var txtChavePublica = new TextBox { Multiline = true, ScrollBars = ScrollBars.Vertical, Location = new Point(xDireita, y + 22), Size = new Size(largura, alturaTexto), Font = new Font("Consolas", 7f), BackColor = Color.FromArgb(250, 250, 255) }; panel.Controls.Add(txtChavePublica); var btnEscolherChave = new Button { Text = "📂 Escolher ficheiro .cer", Location = new Point(xDireita, y + 22 + alturaTexto + 6), Size = new Size(220, 30), BackColor = Color.FromArgb(100, 130, 180), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, Cursor = Cursors.Hand }; btnEscolherChave.FlatAppearance.BorderSize = 0; panel.Controls.Add(btnEscolherChave); // Avançar y para baixo dos dois text boxes y += 22 + alturaTexto + 44; // --- Password + Botão Importar --- // AddSectionLabel(panel, "Passo 2 — Password para o PFX", ref y); var lblPass = new Label { Text = "Password:", Location = new Point(20, y + 3), Size = new Size(80, 22), ForeColor = Color.FromArgb(60, 60, 80), Visible = false }; var txtPass = new TextBox { Location = new Point(110, y), Size = new Size(200, 26), PasswordChar = '●', Text = PASSWORD_DEFAULT, Enabled = false, Visible = false }; panel.Controls.AddRange(new Control[] { lblPass, txtPass }); y += 44; var btnImportar = new Button { Text = "▶ Importar e Gerar PFX", Location = new Point(20, y), Size = new Size(220, 38), BackColor = Color.FromArgb(50, 100, 200), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, Font = new Font("Segoe UI", 10f, FontStyle.Bold), Cursor = Cursors.Hand }; btnImportar.FlatAppearance.BorderSize = 0; panel.Controls.Add(btnImportar); // Eventos btnEscolherCrt.Click += (s, e) => { using var dlg = new OpenFileDialog { Title = "Escolher certificado .crt / .cer", Filter = "Certificados|*.crt;*.cer|Todos|*.*" }; if (dlg.ShowDialog() == DialogResult.OK) txtCrt.Text = File.ReadAllText(dlg.FileName); }; btnEscolherChave.Click += (s, e) => { using var dlg = new OpenFileDialog { Title = "Escolher ChavePublicaAT.cer", Filter = "Certificados|*.cer;*.crt|Todos|*.*" }; if (dlg.ShowDialog() == DialogResult.OK) txtChavePublica.Text = File.ReadAllText(dlg.FileName); }; btnImportar.Click += (s, e) => ImportarCertificadoEChavePublica(txtCrt.Text, txtChavePublica.Text, txtPass.Text); tabImportar.Controls.Add(panel); } // ------------------------------------------------------- // TAB 3 — EXPORTAR FICHEIROS FINAIS // ------------------------------------------------------- private void BuildTabExportar() { var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(20) }; int y = 20; AddSectionLabel(panel, "Exportar .pfx e .cer para a pasta do software", ref y); // Resumo dos ficheiros disponíveis var lblInfo = new Label { Text = "Clique em 'Exportar' para escolher a pasta de destino e copiar os ficheiros finais.", Location = new Point(20, y), Size = new Size(700, 40), ForeColor = Color.FromArgb(80, 80, 100) }; panel.Controls.Add(lblInfo); y += 50; // Ficheiros a exportar lstFicheiros = new ListBox { Location = new Point(20, y), Size = new Size(740, 100), Font = new Font("Consolas", 9f), BackColor = Color.FromArgb(250, 250, 255) }; panel.Controls.Add(lstFicheiros); y += 112; var btnRefresh = new Button { Text = "🔄 Verificar ficheiros", Location = new Point(20, y), Size = new Size(180, 30), BackColor = Color.FromArgb(100, 130, 180), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, Cursor = Cursors.Hand }; btnRefresh.FlatAppearance.BorderSize = 0; panel.Controls.Add(btnRefresh); y += 50; var btnExportar = new Button { Text = "▶ Exportar Ficheiros Finais", Location = new Point(20, y), Size = new Size(260, 44), BackColor = Color.FromArgb(50, 150, 80), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, Font = new Font("Segoe UI", 11f, FontStyle.Bold), Cursor = Cursors.Hand }; btnExportar.FlatAppearance.BorderSize = 0; panel.Controls.Add(btnExportar); RefreshFicheiros(); // Eventos btnRefresh.Click += (s, e) => RefreshFicheiros(lstFicheiros); btnExportar.Click += (s, e) => ExportarFicheirosFinais(); // Carregar ao abrir o tab tabExportar.Enter += (s, e) => RefreshFicheiros(lstFicheiros); tabExportar.Controls.Add(panel); } // ------------------------------------------------------- // LÓGICA — GERAR CSR // ------------------------------------------------------- private void GerarCSR(string nif, string pais, string distrito, string concelho, string org, string dept, string email, out string csrPem) { csrPem = null; try { Log("A gerar chave privada RSA 4096 bits..."); Application.DoEvents(); var keyGenerator = new RsaKeyPairGenerator(); keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), 4096)); var keyPair = keyGenerator.GenerateKeyPair(); string chavePrivadaPath = Path.Combine(DIRETORIA_BASE, $"{nif}.key"); using (var writer = new StreamWriter(chavePrivadaPath)) new PemWriter(writer).WriteObject(keyPair.Private); Log($"Chave privada guardada: {chavePrivadaPath}"); // DN var attrs = new List<DerObjectIdentifier>(); var values = new List<string>(); attrs.Add(X509Name.C); values.Add(pais); attrs.Add(X509Name.ST); values.Add(distrito); attrs.Add(X509Name.L); values.Add(concelho); attrs.Add(X509Name.O); values.Add(org); attrs.Add(X509Name.OU); values.Add(dept); attrs.Add(X509Name.CN); values.Add(nif); attrs.Add(X509Name.EmailAddress); values.Add(email); var subjectDN = new X509Name(attrs, values); Log("A gerar CSR..."); var extensionAttributes = new DerSet(new Asn1EncodableVector()); var csr = new Pkcs10CertificationRequest("SHA256withRSA", subjectDN, keyPair.Public, extensionAttributes, keyPair.Private); if (!csr.Verify()) throw new Exception("Falha na verificação do CSR"); using (var sw = new StringWriter()) { new PemWriter(sw).WriteObject(csr); csrPem = sw.ToString(); } string csrPath = Path.Combine(DIRETORIA_BASE, $"{nif}.csr"); File.WriteAllText(csrPath, csrPem); Log($"CSR guardado: {csrPath}"); Log("✔ CSR gerado com sucesso. Copie e cole no portal da AT."); MessageBox.Show("CSR gerado com sucesso!\n\nCopie o conteúdo e cole no portal da AT.", "Sucesso", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { Log($"ERRO: {ex.Message}"); MessageBox.Show($"Erro ao gerar CSR:\n{ex.Message}", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // ------------------------------------------------------- // LÓGICA — IMPORTAR CRT + CHAVE PÚBLICA + GERAR PFX // ------------------------------------------------------- private void ImportarCertificadoEChavePublica(string conteudoCrt, string conteudoChavePublica, string password) { try { if (string.IsNullOrWhiteSpace(conteudoCrt) || !conteudoCrt.Contains("-----BEGIN CERTIFICATE-----")) { MessageBox.Show("Certificado .crt inválido ou em falta.", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (string.IsNullOrWhiteSpace(conteudoChavePublica) || !conteudoChavePublica.Contains("-----BEGIN CERTIFICATE-----")) { MessageBox.Show("Chave Pública AT inválida ou em falta.", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (string.IsNullOrWhiteSpace(password)) { MessageBox.Show("Introduza uma password para o PFX.", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } string chavePrivadaPath = Path.Combine(DIRETORIA_BASE, $"{NUMERO_CONTRIBUINTE}.key"); if (!File.Exists(chavePrivadaPath)) { MessageBox.Show($"Chave privada não encontrada:\n{chavePrivadaPath}\n\nGere primeiro o CSR (passo 1).", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // Guardar CRT string destinoCrt = Path.Combine(DIRETORIA_BASE, $"{NUMERO_CONTRIBUINTE}.crt"); File.WriteAllText(destinoCrt, conteudoCrt.Trim()); Log($"CRT guardado: {destinoCrt}"); // Guardar Chave Pública string destinoChavePublica = Path.Combine(DIRETORIA_BASE, "ChavePublicaAT.cer"); File.WriteAllText(destinoChavePublica, conteudoChavePublica.Trim()); Log($"Chave Pública AT guardada: {destinoChavePublica}"); // Gerar PFX Log("A gerar PFX..."); string pfxPath = GerarPFX(chavePrivadaPath, destinoCrt, password); Log($"✔ PFX gerado: {pfxPath}"); MessageBox.Show($"PFX gerado com sucesso!\n\n{pfxPath}\n\n", "Sucesso", MessageBoxButtons.OK, MessageBoxIcon.Information); tabControl.SelectedTab = tabExportar; } catch (Exception ex) { Log($"ERRO: {ex.Message}"); MessageBox.Show($"Erro:\n{ex.Message}", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // ------------------------------------------------------- // LÓGICA — EXPORTAR FICHEIROS FINAIS // ------------------------------------------------------- private void ExportarFicheirosFinais() { try { string pfxPath = Path.Combine(DIRETORIA_BASE, $"{NUMERO_CONTRIBUINTE}.pfx"); string chavePublicaPath = Path.Combine(DIRETORIA_BASE, "ChavePublicaAT.cer"); if (!File.Exists(pfxPath)) { MessageBox.Show($"Ficheiro PFX não encontrado:\n{pfxPath}", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!File.Exists(chavePublicaPath)) { MessageBox.Show($"Chave Pública AT não encontrada:\n{chavePublicaPath}", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } using var dlg = new FolderBrowserDialog { Description = "Escolher pasta de destino para .pfx e .cer", UseDescriptionForTitle = true, ShowNewFolderButton = true, InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) }; if (dlg.ShowDialog() != DialogResult.OK) return; string filePfx = Path.Combine(dlg.SelectedPath, $"{NOMEFINAL}.pfx"); string fileCer = Path.Combine(dlg.SelectedPath, $"{NOMEFINAL}.cer"); File.Copy(pfxPath, filePfx, true); File.Copy(chavePublicaPath, fileCer, true); Log($"✔ {NOMEFINAL}.pfx exportado: {filePfx}"); Log($"✔ {NOMEFINAL}.cer exportado: {fileCer}"); MessageBox.Show($"Ficheiros exportados com sucesso!\n\n{filePfx}\n{fileCer}", "Sucesso", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { Log($"ERRO: {ex.Message}"); MessageBox.Show($"Erro:\n{ex.Message}", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // ------------------------------------------------------- // LÓGICA — GERAR PFX // ------------------------------------------------------- private string GerarPFX(string caminhoChavePrivada, string caminhoCertificado, string password) { AsymmetricKeyParameter chavePrivada; using (var reader = File.OpenText(caminhoChavePrivada)) { var obj = new PemReader(reader).ReadObject(); chavePrivada = obj is AsymmetricCipherKeyPair kp ? kp.Private : obj is RsaPrivateCrtKeyParameters rsa ? rsa : throw new Exception("Formato de chave privada não suportado"); } X509Certificate certificado; using (var reader = File.OpenText(caminhoCertificado)) certificado = (X509Certificate)new PemReader(reader).ReadObject(); var store = new Pkcs12StoreBuilder().Build(); store.SetKeyEntry(NUMERO_CONTRIBUINTE, new AsymmetricKeyEntry(chavePrivada), new[] { new X509CertificateEntry(certificado) }); string pfxPath = Path.Combine(DIRETORIA_BASE, $"{NUMERO_CONTRIBUINTE}.pfx"); using (var fs = new FileStream(pfxPath, FileMode.Create, FileAccess.Write)) store.Save(fs, password.ToCharArray(), new SecureRandom()); return pfxPath; } // ------------------------------------------------------- // AUXILIARES // ------------------------------------------------------- private void RefreshFicheiros(ListBox lst = null) { var lista = lst ?? lstFicheiros; lista.Items.Clear(); var ficheiros = new[] { ($"{NUMERO_CONTRIBUINTE}.key", "Chave Privada"), ($"{NUMERO_CONTRIBUINTE}.csr", "Pedido de Certificado"), ($"{NUMERO_CONTRIBUINTE}.crt", "Certificado AT"), ("ChavePublicaAT.cer", "Chave Pública AT"), ($"{NUMERO_CONTRIBUINTE}.pfx", "Ficheiro PFX"), }; foreach (var (ficheiro, descricao) in ficheiros) { string caminho = Path.Combine(DIRETORIA_BASE, ficheiro); string estado = File.Exists(caminho) ? "✔" : "✗"; lista.Items.Add($" {estado} {descricao,-28} {ficheiro}"); } } private void AddSectionLabel(Panel panel, string texto, ref int y) { var lbl = new Label { Text = texto, Font = new Font("Segoe UI", 10f, FontStyle.Bold), ForeColor = Color.FromArgb(50, 50, 120), Location = new Point(20, y), AutoSize = true }; var sep = new Panel { Location = new Point(20, y + 22), Size = new Size(740, 1), BackColor = Color.FromArgb(180, 180, 210) }; panel.Controls.Add(lbl); panel.Controls.Add(sep); y += 34; } private void Log(string mensagem) { string linha = $"{mensagem}"; txtLog.AppendText(linha + Environment.NewLine); txtLog.ScrollToCaret(); } } } Edited March 18, 2026 at 06:47 PM by cjulio 3 Report
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now