LevelOS-Core/TLL/Certificadohelper.cs

213 lines
10 KiB
C#

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using MLL;
namespace TLL
{
/// <summary>
/// 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.
/// </summary>
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; }
}
}