Skip to content

Instantly share code, notes, and snippets.

@carloswm85
Created September 25, 2025 13:01
Show Gist options
  • Save carloswm85/a216cbb7895c11954bf0f84a3ca8a8b8 to your computer and use it in GitHub Desktop.
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…
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