Created
September 25, 2025 13:01
-
-
Save carloswm85/a216cbb7895c11954bf0f84a3ca8a8b8 to your computer and use it in GitHub Desktop.
The `PdfProtection` class is a utility for creating and managing password-protected PDF documents using iText 7, offering configurable security through encryption levels (40-bit, 128-bit, AES-128, AES-256) and fine-grained permission flags (printing, copying, modifying, annotating, filling forms, etc.). It allows both synchronous and asynchronou…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.IO; | |
| using System.Security.Cryptography; | |
| using System.Text; | |
| using System.Threading.Tasks; | |
| using iText.Kernel.Exceptions; | |
| using iText.Kernel.Pdf; | |
| using iText.Kernel.Pdf.Canvas.Draw; | |
| using iText.Layout; | |
| using iText.Layout.Element; | |
| using iText.Layout.Properties; | |
| namespace MiningTracker.Service.Services.Helpers | |
| { | |
| /// <summary> | |
| /// Enhanced PDF protection utility with improved security and flexibility | |
| /// </summary> | |
| public class PdfProtection : IDisposable | |
| { | |
| private bool _disposed = false; | |
| /// <summary> | |
| /// PDF permission flags that can be combined | |
| /// </summary> | |
| [Flags] | |
| public enum PdfPermissions | |
| { | |
| None = 0, | |
| Print = EncryptionConstants.ALLOW_PRINTING, | |
| PrintHighQuality = EncryptionConstants.ALLOW_PRINTING | EncryptionConstants.ALLOW_DEGRADED_PRINTING, | |
| Copy = EncryptionConstants.ALLOW_COPY, | |
| Modify = EncryptionConstants.ALLOW_MODIFY_CONTENTS, | |
| ModifyAnnotations = EncryptionConstants.ALLOW_MODIFY_ANNOTATIONS, | |
| FillForm = EncryptionConstants.ALLOW_FILL_IN, | |
| ScreenReaders = EncryptionConstants.ALLOW_SCREENREADERS, | |
| Assembly = EncryptionConstants.ALLOW_ASSEMBLY, | |
| All = Print | Copy | Modify | ModifyAnnotations | FillForm | ScreenReaders | Assembly | |
| } | |
| /// <summary> | |
| /// Encryption strength options | |
| /// </summary> | |
| public enum EncryptionLevel | |
| { | |
| Standard40Bit = EncryptionConstants.STANDARD_ENCRYPTION_40, | |
| Standard128Bit = EncryptionConstants.STANDARD_ENCRYPTION_128, | |
| AES128 = EncryptionConstants.ENCRYPTION_AES_128, | |
| AES256 = EncryptionConstants.ENCRYPTION_AES_256 | |
| } | |
| /// <summary> | |
| /// Configuration for PDF protection | |
| /// </summary> | |
| public class PdfProtectionConfig | |
| { | |
| public string UserPassword { get; set; } = string.Empty; | |
| public string OwnerPassword { get; set; } = string.Empty; | |
| public PdfPermissions Permissions { get; set; } = PdfPermissions.Print; | |
| public EncryptionLevel Encryption { get; set; } = EncryptionLevel.AES256; | |
| public bool GenerateRandomOwnerPassword { get; set; } = true; | |
| public string Title { get; set; } = "Protected Document"; | |
| public string Content { get; set; } = "Este documento está protegido con contraseña."; | |
| } | |
| /// <summary> | |
| /// Creates a password-protected PDF with enhanced security options | |
| /// </summary> | |
| /// <param name="filePath">Output file path</param> | |
| /// <param name="config">Protection configuration</param> | |
| /// <returns>Generated owner password if auto-generated</returns> | |
| public async Task<string> CreateProtectedPdfAsync(string filePath, PdfProtectionConfig config) | |
| { | |
| ValidateInputs(filePath, config); | |
| string actualOwnerPassword = config.OwnerPassword; | |
| // Generate secure owner password if requested | |
| if (config.GenerateRandomOwnerPassword || string.IsNullOrEmpty(config.OwnerPassword)) | |
| { | |
| actualOwnerPassword = GenerateSecurePassword(16); | |
| } | |
| try | |
| { | |
| // Ensure directory exists | |
| var directory = Path.GetDirectoryName(filePath); | |
| if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) | |
| { | |
| Directory.CreateDirectory(directory); | |
| } | |
| await Task.Run(() => CreatePdfDocument(filePath, config, actualOwnerPassword)); | |
| return actualOwnerPassword; | |
| } | |
| catch (Exception ex) | |
| { | |
| throw new PdfProtectionException($"Error creating protected PDF: {ex.Message}", ex); | |
| } | |
| } | |
| /// <summary> | |
| /// Synchronous version of CreateProtectedPdfAsync | |
| /// </summary> | |
| public string CreateProtectedPdf(string filePath, PdfProtectionConfig config) | |
| { | |
| return CreateProtectedPdfAsync(filePath, config).GetAwaiter().GetResult(); | |
| } | |
| /// <summary> | |
| /// Creates a simple protected PDF with default settings | |
| /// </summary> | |
| public async Task<string> CreateSimpleProtectedPdfAsync(string filePath, string userPassword, string content = null) | |
| { | |
| var config = new PdfProtectionConfig | |
| { | |
| UserPassword = userPassword, | |
| Content = content ?? "Este PDF está protegido con contraseña.", | |
| Permissions = PdfPermissions.Print | PdfPermissions.Copy, | |
| GenerateRandomOwnerPassword = true | |
| }; | |
| return await CreateProtectedPdfAsync(filePath, config); | |
| } | |
| /// <summary> | |
| /// Validates a PDF's protection status | |
| /// </summary> | |
| public static bool IsPdfProtected(string filePath) | |
| { | |
| try | |
| { | |
| using (var reader = new PdfReader(filePath)) | |
| { | |
| return reader.IsEncrypted(); | |
| } | |
| } | |
| catch (BadPasswordException) | |
| { | |
| return true; // Protected and we don't have the password | |
| } | |
| catch (Exception) | |
| { | |
| return false; // Assume unprotected if we can't determine | |
| } | |
| } | |
| private void CreatePdfDocument(string filePath, PdfProtectionConfig config, string ownerPassword) | |
| { | |
| byte[] userPasswordBytes = Encoding.UTF8.GetBytes(config.UserPassword); | |
| byte[] ownerPasswordBytes = Encoding.UTF8.GetBytes(ownerPassword); | |
| var writerProperties = new WriterProperties() | |
| .SetStandardEncryption( | |
| userPasswordBytes, | |
| ownerPasswordBytes, | |
| (int)config.Permissions, | |
| (int)config.Encryption | |
| ); | |
| using (var writer = new PdfWriter(filePath, writerProperties)) | |
| using (var pdf = new PdfDocument(writer)) | |
| using (var document = new Document(pdf)) | |
| { | |
| // Set document metadata | |
| var info = pdf.GetDocumentInfo(); | |
| info.SetTitle(config.Title); | |
| info.SetCreator("PDF Protection Utility"); | |
| info.SetProducer("iText 7"); | |
| // Add content with better formatting | |
| AddDocumentContent(document, config); | |
| } | |
| } | |
| private void AddDocumentContent(Document document, PdfProtectionConfig config) | |
| { | |
| // Title | |
| var title = new Paragraph(config.Title) | |
| .SetTextAlignment(TextAlignment.CENTER) | |
| .SetFontSize(20) | |
| .SimulateBold() | |
| .SetMarginBottom(20); | |
| document.Add(title); | |
| // Separator line | |
| var line = new LineSeparator(new SolidLine()); | |
| document.Add(line); | |
| // Content | |
| var content = new Paragraph(config.Content) | |
| .SetTextAlignment(TextAlignment.JUSTIFIED) | |
| .SetMarginTop(20) | |
| .SetMarginBottom(20); | |
| document.Add(content); | |
| // Security info | |
| var securityInfo = new Paragraph() | |
| .Add("Información de Seguridad:\n") | |
| .SimulateBold() | |
| .SetMarginTop(30); | |
| securityInfo.Add(new Text($"• Nivel de cifrado: {GetEncryptionLevelDescription(config.Encryption)}\n").SimulateBold()); | |
| securityInfo.Add(new Text($"• Permisos: {GetPermissionsDescription(config.Permissions)}\n").SimulateBold()); | |
| securityInfo.Add(new Text($"• Fecha de creación: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\n").SimulateBold()); | |
| document.Add(securityInfo); | |
| } | |
| private string GetEncryptionLevelDescription(EncryptionLevel level) | |
| { | |
| switch (level) | |
| { | |
| case EncryptionLevel.Standard40Bit: | |
| return "Estándar 40-bit (Baja)"; | |
| case EncryptionLevel.Standard128Bit: | |
| return "Estándar 128-bit (Media)"; | |
| case EncryptionLevel.AES128: | |
| return "AES 128-bit (Alta)"; | |
| case EncryptionLevel.AES256: | |
| return "AES 256-bit (Muy Alta)"; | |
| default: | |
| return "Desconocido"; | |
| } | |
| } | |
| private string GetPermissionsDescription(PdfPermissions permissions) | |
| { | |
| if (permissions == PdfPermissions.None) | |
| return "Sin permisos"; | |
| if (permissions == PdfPermissions.All) | |
| return "Todos los permisos"; | |
| var permissionList = new List<string>(); | |
| if (permissions.HasFlag(PdfPermissions.Print)) | |
| permissionList.Add("Imprimir"); | |
| if (permissions.HasFlag(PdfPermissions.Copy)) | |
| permissionList.Add("Copiar"); | |
| if (permissions.HasFlag(PdfPermissions.Modify)) | |
| permissionList.Add("Modificar"); | |
| if (permissions.HasFlag(PdfPermissions.ModifyAnnotations)) | |
| permissionList.Add("Anotar"); | |
| if (permissions.HasFlag(PdfPermissions.FillForm)) | |
| permissionList.Add("Rellenar formularios"); | |
| return string.Join(", ", permissionList); | |
| } | |
| private static string GenerateSecurePassword(int length) | |
| { | |
| const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*"; | |
| using (var rng = RandomNumberGenerator.Create()) | |
| { | |
| var data = new byte[length]; | |
| rng.GetBytes(data); | |
| var result = new StringBuilder(length); | |
| for (int i = 0; i < length; i++) | |
| { | |
| result.Append(chars[data[i] % chars.Length]); | |
| } | |
| return result.ToString(); | |
| } | |
| } | |
| private static void ValidateInputs(string filePath, PdfProtectionConfig config) | |
| { | |
| if (string.IsNullOrWhiteSpace(filePath)) | |
| throw new ArgumentException("File path cannot be null or empty", nameof(filePath)); | |
| if (config == null) | |
| throw new ArgumentNullException(nameof(config)); | |
| if (string.IsNullOrEmpty(config.UserPassword)) | |
| throw new ArgumentException("User password cannot be null or empty", nameof(config.UserPassword)); | |
| // Validate file extension | |
| var extension = Path.GetExtension(filePath)?.ToLowerInvariant(); | |
| if (extension != ".pdf") | |
| throw new ArgumentException("File must have .pdf extension", nameof(filePath)); | |
| } | |
| public void Dispose() | |
| { | |
| Dispose(true); | |
| GC.SuppressFinalize(this); | |
| } | |
| protected virtual void Dispose(bool disposing) | |
| { | |
| if (!_disposed) | |
| { | |
| if (disposing) | |
| { | |
| // Cleanup managed resources if any | |
| } | |
| _disposed = true; | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Custom exception for PDF protection operations | |
| /// </summary> | |
| public class PdfProtectionException : Exception | |
| { | |
| public PdfProtectionException(string message) : base(message) { } | |
| public PdfProtectionException(string message, Exception innerException) : base(message, innerException) { } | |
| } | |
| /// <summary> | |
| /// Example usage class | |
| /// </summary> | |
| public static class PdfProtectionExamples | |
| { | |
| public static async Task BasicUsageExample() | |
| { | |
| using (var pdfProtection = new PdfProtection()) | |
| { | |
| // Simple protected PDF | |
| string ownerPassword = await pdfProtection.CreateSimpleProtectedPdfAsync( | |
| "C:\\temp\\simple_protected.pdf", | |
| "usuario123" | |
| ); | |
| Console.WriteLine($"PDF created. Owner password: {ownerPassword}"); | |
| } | |
| } | |
| public static async Task AdvancedUsageExample() | |
| { | |
| using (var pdfProtection = new PdfProtection()) | |
| { | |
| var config = new PdfProtection.PdfProtectionConfig | |
| { | |
| UserPassword = "mi_password_segura", | |
| Title = "Documento Confidencial", | |
| Content = "Este es un documento altamente confidencial con contenido sensible.", | |
| Permissions = PdfProtection.PdfPermissions.Print | PdfProtection.PdfPermissions.Copy, | |
| Encryption = PdfProtection.EncryptionLevel.AES256, | |
| GenerateRandomOwnerPassword = true | |
| }; | |
| string ownerPassword = await pdfProtection.CreateProtectedPdfAsync( | |
| "C:\\temp\\advanced_protected.pdf", | |
| config | |
| ); | |
| Console.WriteLine($"Advanced PDF created. Owner password: {ownerPassword}"); | |
| // Check if PDF is protected | |
| bool isProtected = PdfProtection.IsPdfProtected("C:\\temp\\advanced_protected.pdf"); | |
| Console.WriteLine($"PDF is protected: {isProtected}"); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment