using System; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using MLL; namespace TLL { /// /// Gera um certificado autoassinado com os dados da empresa. /// Salva .pfx (com chave privada) e .cer (chave pública) em C:\LevelCode\LevelOS\Cert /// Validade: 1 ano a partir da geração. /// public static class CertificadoHelper { private static readonly string PastaDestino = @"C:\LevelCode\LevelOS\Cert"; // ══════════════════════════════════════════════════════════════════════ // GERAR CERTIFICADO // ══════════════════════════════════════════════════════════════════════ public static CertificadoResultado Gerar(ModeloEmpresa empresa, string senhaPfx) { try { // Garante que a pasta existe Directory.CreateDirectory(PastaDestino); // Limpa campos nulos antes de usar string nome = empresa.Nome?.Trim() ?? "Empresa"; string uf = empresa.UF?.Trim() ?? "BR"; string cidade = empresa.Cidade?.Trim() ?? ""; string email = empresa.Email?.Trim() ?? ""; string cnpjLimpo = empresa.CNPJ? .Replace(".", "").Replace("/", "").Replace("-", "").Trim() ?? "empresa"; // ── Monta o Distinguished Name (DN) ──────────────────────────── var dn = new X500DistinguishedName( $"CN={nome}, " + $"O={nome}, " + $"OU=LevelOS, " + $"C=BR, " + $"ST={uf}, " + $"L={cidade}, " + $"SERIALNUMBER={cnpjLimpo}"); // ── Gera chave RSA 2048 bits ─────────────────────────────────── using var rsa = RSA.Create(2048); // ── Configura o certificado ──────────────────────────────────── var request = new CertificateRequest(dn, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); // Extensões request.CertificateExtensions.Add( new X509BasicConstraintsExtension(false, false, 0, true)); request.CertificateExtensions.Add( new X509KeyUsageExtension( X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation | X509KeyUsageFlags.KeyEncipherment, true)); request.CertificateExtensions.Add( new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.2"), // Client Authentication new Oid("1.3.6.1.5.5.7.3.4") // Email Protection }, false)); // Compatível com .NET 8 request.CertificateExtensions.Add( new X509SubjectKeyIdentifierExtension(request.PublicKey, false)); // ── Validade: hoje até 1 ano ─────────────────────────────────── var agora = DateTimeOffset.UtcNow; var expira = agora.AddYears(1); // ── Gera o certificado autoassinado ──────────────────────────── var cert = request.CreateSelfSigned(agora, expira); // ── Caminhos dos arquivos ────────────────────────────────────── string nomePfx = Path.Combine(PastaDestino, $"{cnpjLimpo}.pfx"); string nomeCer = Path.Combine(PastaDestino, $"{cnpjLimpo}.cer"); // ── Exporta .pfx (chave pública + privada, protegido por senha) byte[] pfxBytes = cert.Export(X509ContentType.Pfx, senhaPfx); File.WriteAllBytes(nomePfx, pfxBytes); // ── Exporta .cer (somente chave pública) ────────────────────── byte[] cerBytes = cert.Export(X509ContentType.Cert); File.WriteAllBytes(nomeCer, cerBytes); return new CertificadoResultado { Sucesso = true, CaminhoPfx = nomePfx, CaminhoCer = nomeCer, Thumbprint = cert.Thumbprint, ValidoAte = expira.LocalDateTime, Mensagem = "Certificado gerado com sucesso!" }; } catch (Exception ex) { return new CertificadoResultado { Sucesso = false, Mensagem = $"Erro ao gerar certificado: {ex.Message}" }; } } // ══════════════════════════════════════════════════════════════════════ // CARREGAR CERTIFICADO EXISTENTE // ══════════════════════════════════════════════════════════════════════ public static X509Certificate2? Carregar(string cnpj, string senhaPfx) { try { string cnpjLimpo = cnpj?.Replace(".", "").Replace("/", "").Replace("-", "") ?? ""; string caminho = Path.Combine(PastaDestino, $"{cnpjLimpo}.pfx"); if (!File.Exists(caminho)) return null; var collection = new X509Certificate2Collection(); collection.Import(caminho, senhaPfx, X509KeyStorageFlags.DefaultKeySet); return collection.Count > 0 ? collection[0] : null; } catch { return null; } } // ══════════════════════════════════════════════════════════════════════ // VERIFICAR SE CERTIFICADO EXISTE E ESTÁ VÁLIDO // ══════════════════════════════════════════════════════════════════════ public static StatusCertificado VerificarStatus(string cnpj, string senhaPfx) { try { string cnpjLimpo = cnpj?.Replace(".", "").Replace("/", "").Replace("-", "") ?? ""; string caminho = Path.Combine(PastaDestino, $"{cnpjLimpo}.pfx"); if (!File.Exists(caminho)) return new StatusCertificado { Existe = false, Mensagem = "Certificado não encontrado." }; var collection = new X509Certificate2Collection(); collection.Import(caminho, senhaPfx, X509KeyStorageFlags.DefaultKeySet); if (collection.Count == 0) return new StatusCertificado { Existe = false, Mensagem = "Certificado inválido ou vazio." }; var cert = collection[0]; var expira = cert.NotAfter; var diasRestantes = (expira - DateTime.Now).Days; return new StatusCertificado { Existe = true, Valido = DateTime.Now <= expira, Thumbprint = cert.Thumbprint, ValidoAte = expira, DiasRestantes = diasRestantes, Mensagem = diasRestantes > 0 ? $"Certificado válido por mais {diasRestantes} dias." : "Certificado expirado!" }; } catch (Exception ex) { return new StatusCertificado { Existe = true, Valido = false, Mensagem = $"Erro ao ler o certificado: {ex.Message}" }; } } } // ══════════════════════════════════════════════════════════════════════════ // MODELOS DE RETORNO // ══════════════════════════════════════════════════════════════════════════ public class CertificadoResultado { public bool Sucesso { get; set; } public string Mensagem { get; set; } = string.Empty; public string? CaminhoPfx { get; set; } public string? CaminhoCer { get; set; } public string? Thumbprint { get; set; } public DateTime ValidoAte { get; set; } } public class StatusCertificado { public bool Existe { get; set; } public bool Valido { get; set; } public string Mensagem { get; set; } = string.Empty; public string? Thumbprint { get; set; } public DateTime ValidoAte { get; set; } public int DiasRestantes { get; set; } } }