Skip to content

yamgooo/Sri.Sign

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

8 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

SRI Digital Signature Service

.NET License NuGet

A professional .NET library for digital signature operations using XAdES signatures, specifically designed for SRI (Servicio de Rentas Internas) electronic invoicing in Ecuador.

Also available in Spanish: README_es.md

πŸš€ Features

  • XAdES Digital Signatures: Full support for XML Advanced Electronic Signatures
  • Certificate Management: Flexible certificate loading from P12/PFX files and Base64
  • Base64 Certificates: Full support for embedded Base64 certificates (ideal for cloud and containers)
  • Async Operations: High-performance asynchronous signature operations
  • Configuration Support: Multiple configuration options (appsettings.json, code-based, dynamic)
  • Validation: Built-in signature validation and certificate verification
  • Logging: Comprehensive logging with structured logging support
  • Error Handling: Robust error handling with detailed error messages
  • Performance Monitoring: Built-in performance metrics and timing

πŸ“¦ Installation

NuGet Package

dotnet add package Yamgooo.SRI.Sign

Manual Installation

git clone https://github.com/yamgooo/Sri.Sign.git
cd Sri.Sign
dotnet build

πŸ› οΈ Quick Start

1. Basic Usage (Dynamic Configuration)

using Yamgooo.SRI.Sign;

// Register the service
services.AddSriSignService();

// Use the service
var sriSignService = serviceProvider.GetRequiredService<ISriSignService>();

// Set certificate dynamically
sriSignService.SetDefaultCertificate("path/to/certificate.p12", "your-password");

// Sign XML content
var result = await sriSignService.SignAsync(xmlContent);

if (result.Success)
{
    Console.WriteLine($"XML signed successfully in {result.ProcessingTimeMs}ms");
    var signedXml = result.SignedXml;
}
else
{
    Console.WriteLine($"Error: {result.ErrorMessage}");
}

2. Direct Certificate Usage

// Sign with specific certificate (no configuration needed)
var result = await sriSignService.SignAsync(
    xmlContent, 
    "path/to/certificate.p12", 
    "your-password"
);

3. Base64 Certificate Usage

// Sign with Base64 certificate (ideal for cloud and containers)
var result = await sriSignService.SignWithBase64CertificateAsync(
    xmlContent, 
    "MIIKsAIBAzCCCmwGCSqGSIb3DQEHAaCCCl0EggpZMIIKVTCCBQ...", 
    "your-password"
);

// Or set default Base64 certificate
sriSignService.SetDefaultBase64Certificate(certificateBase64, password);
var result = await sriSignService.SignWithDefaultBase64CertificateAsync(xmlContent);

4. Configuration-based Usage

appsettings.json

{
  "SriSign": {
    "CertificatePath": "Certificates/certificate.p12",
    "CertificatePassword": "your-secure-password",
    "CertificateBase64": "MIIKsAIBAzCCCmwGCSqGSIb3DQEHAaCCCl0EggpZMIIKVTCCBQ...",
    "Base64CertificatePassword": "your-base64-password"
  }
}

Program.cs

// Register with configuration
services.AddSriSignService(configuration);

// Use the service
var sriSignService = serviceProvider.GetRequiredService<ISriSignService>();
var result = await sriSignService.SignAsync(xmlContent);

5. Custom Configuration

var config = new SriSignConfiguration
{
    CertificatePath = "path/to/certificate.p12",
    CertificatePassword = "your-password",
    CertificateBase64 = "MIIKsAIBAzCCCmwGCSqGSIb3DQEHAaCCCl0EggpZMIIKVTCCBQ...",
    Base64CertificatePassword = "your-base64-password"
};

services.AddSriSignService(config);

πŸ“‹ API Reference

ISriSignService Interface

SignAsync Methods

// Sign with default certificate configuration (prioritizes Base64 if configured)
Task<SignatureResult> SignAsync(string xmlContent);

// Sign with specific certificate
Task<SignatureResult> SignAsync(string xmlContent, string certificatePath, string password);

// Sign with specific Base64 certificate
Task<SignatureResult> SignWithBase64CertificateAsync(string xmlContent, string certificateBase64, string password);

// Sign with default Base64 certificate
Task<SignatureResult> SignWithDefaultBase64CertificateAsync(string xmlContent);

Validation

// Validate a signed XML document
bool ValidateSignature(string signedXml);

// Validate a Base64 certificate
bool ValidateBase64Certificate(string certificateBase64, string password);

Configuration

// Set default certificate for subsequent operations
void SetDefaultCertificate(string certificatePath, string password);

// Set default Base64 certificate for subsequent operations
void SetDefaultBase64Certificate(string certificateBase64, string password);

SignatureResult

public class SignatureResult
{
    public bool Success { get; set; }
    public string SignedXml { get; set; }
    public string ErrorMessage { get; set; }
    public DateTime SignatureTimestamp { get; set; }
    public long ProcessingTimeMs { get; set; }
}

πŸ”§ Configuration Options

Service Registration Methods

// From appsettings.json
services.AddSriSignService(configuration, "SriSign");

// With custom configuration object
services.AddSriSignService(customConfig);

// With direct certificate parameters
services.AddSriSignService("certificate.p12", "password");

// Without configuration (for dynamic use)
services.AddSriSignService();

Configuration Validation

The service automatically validates:

  • Certificate file existence and accessibility
  • Certificate file size (1KB - 10MB)
  • Certificate password validity
  • Certificate expiration
  • Private key availability
  • Valid Base64 format for embedded certificates
  • Automatic Base64 format cleaning (line breaks, PEM headers, etc.)

πŸ“ Examples

Base64 Certificate Example

public class Base64CertificateExample
{
    private readonly ISriSignService _sriSignService;

    public async Task<SignatureResult> SignWithBase64CertificateAsync(string xmlContent)
    {
        // Convert certificate file to Base64 (one-time operation)
        var certificateBytes = File.ReadAllBytes("certificate.p12");
        var certificateBase64 = Convert.ToBase64String(certificateBytes);
        var password = "your-password";

        // Validate Base64 certificate before using
        if (!_sriSignService.ValidateBase64Certificate(certificateBase64, password))
        {
            throw new InvalidOperationException("Invalid Base64 certificate");
        }

        // Sign with Base64 certificate
        return await _sriSignService.SignWithBase64CertificateAsync(xmlContent, certificateBase64, password);
    }

    public async Task<SignatureResult> SignWithConfiguredBase64Async(string xmlContent)
    {
        // Configure default Base64 certificate
        var certificateBase64 = Environment.GetEnvironmentVariable("SRI_CERTIFICATE_BASE64");
        var password = Environment.GetEnvironmentVariable("SRI_CERTIFICATE_PASSWORD");
        
        _sriSignService.SetDefaultBase64Certificate(certificateBase64, password);
        
        // Sign using default configuration
        return await _sriSignService.SignAsync(xmlContent);
    }
}

Complete SRI Invoice Signing Example

public class InvoiceSigningService
{
    private readonly ISriSignService _sriSignService;
    private readonly ILogger<InvoiceSigningService> _logger;

    public InvoiceSigningService(ISriSignService sriSignService, ILogger<InvoiceSigningService> logger)
    {
        _sriSignService = sriSignService;
        _logger = logger;
    }

    public async Task<SignatureResult> SignInvoiceAsync(string invoiceXml)
    {
        try
        {
            // Sign the invoice XML
            var result = await _sriSignService.SignAsync(invoiceXml);
            
            if (result.Success)
            {
                _logger.LogInformation("Invoice signed successfully");
                
                // Validate the signature
                if (_sriSignService.ValidateSignature(result.SignedXml))
                {
                    _logger.LogInformation("Signature validation passed");
                }
                else
                {
                    _logger.LogWarning("Signature validation failed");
                }
            }
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error signing invoice");
            return SignatureResult.CreateFailure(ex.Message);
        }
    }
}

Multiple Certificate Management

public class MultiTenantSigningService
{
    private readonly ISriSignService _sriSignService;

    public async Task<SignatureResult> SignForTenantAsync(string xmlContent, string tenantId)
    {
        // Get tenant-specific certificate
        var certificatePath = GetTenantCertificatePath(tenantId);
        var certificatePassword = GetTenantCertificatePassword(tenantId);

        // Sign with tenant-specific certificate
        return await _sriSignService.SignAsync(xmlContent, certificatePath, certificatePassword);
    }
}

πŸ”’ Security Considerations

  • Certificate Storage: Store certificates securely and never commit them to source control
  • Base64 Certificates: Ideal for containers and cloud services, store in environment variables or secret services
  • Password Management: Use secure configuration providers (Azure Key Vault, AWS Secrets Manager, etc.)
  • File Permissions: Ensure certificate files have appropriate access permissions
  • Network Security: Use HTTPS for all network communications
  • Logging: Be careful not to log sensitive information like certificate passwords
  • Certificate Rotation: Consider rotating certificates regularly, especially in production environments

πŸ§ͺ Testing

Unit Testing Example

[Test]
public async Task SignAsync_WithValidXml_ReturnsSuccess()
{
    // Arrange
    var mockLogger = new Mock<ILogger<SriSignService>>();
    var service = new SriSignService(mockLogger.Object);
    
    var xmlContent = "<test>content</test>";
    var certificatePath = "test-cert.p12";
    var password = "test-password";

    // Act
    var result = await service.SignAsync(xmlContent, certificatePath, password);

    // Assert
    Assert.IsNotNull(result);
    // Add more specific assertions based on your test certificate
}

πŸš€ Performance

The service is optimized for high-performance operations:

  • Async Operations: All I/O operations are asynchronous
  • Memory Efficient: Uses streaming for large XML documents
  • Caching: Certificate loading is optimized
  • Metrics: Built-in performance monitoring

Typical performance metrics:

  • Small XML (< 1KB): ~50-100ms
  • Medium XML (1-10KB): ~100-200ms
  • Large XML (10-100KB): ~200-500ms

πŸ“¦ Dependencies

  • .NET 9.0: Target framework
  • FirmaXadesNet: XAdES signature implementation
  • Microsoft.Extensions.Configuration: Configuration support
  • Microsoft.Extensions.Logging: Logging infrastructure
  • Microsoft.Extensions.Options: Options pattern support

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ“ž Support


Made with ❀️ for the Ecuadorian developer community

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages