AMN.PdfSignLibrary 1.0.0

dotnet add package AMN.PdfSignLibrary --version 1.0.0
                    
NuGet\Install-Package AMN.PdfSignLibrary -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AMN.PdfSignLibrary" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AMN.PdfSignLibrary" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="AMN.PdfSignLibrary" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AMN.PdfSignLibrary --version 1.0.0
                    
#r "nuget: AMN.PdfSignLibrary, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AMN.PdfSignLibrary@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AMN.PdfSignLibrary&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=AMN.PdfSignLibrary&version=1.0.0
                    
Install as a Cake Tool

AMN Digital Signature Library

Library .NET untuk menandatangani dokumen PDF dengan Digital Signature dan QR Code yang terdeteksi oleh Adobe Acrobat Reader.

✨ Fitur Utama

  • Digital Signature: Cryptographic signature yang terdeteksi di Adobe Acrobat
  • QR Code Integration: QR code ditampilkan sebagai visual appearance signature
  • Certificate Information: Menampilkan informasi certificate saat QR code diklik
  • Auto-positioning: Validasi dan adjustment posisi signature agar tidak keluar dari halaman
  • Incremental File Numbering: Otomatis membuat file baru dengan numbering (filename_signed_1.pdf, filename_signed_2.pdf, dst) untuk mencegah overwrite
  • Self-signed Support: Support untuk development dengan self-signed certificate
  • Production Ready: Support untuk production certificate dari Certificate Authority
  • Multi-framework: .NET 8.0 dan .NET 7.0

📦 Instalasi

dotnet add package AmnDigitalSignature

🚀 Quick Start

1. Generate Development Certificate

Untuk development, Anda perlu generate self-signed certificate (.pfx). Jalankan script PowerShell berikut:

# Buka PowerShell di folder CERT
cd CERT

# Jalankan script generate certificate
.\Generating-pfx.ps1

# Script akan meminta password untuk .pfx file
# Simpan password ini untuk digunakan di konfigurasi

Script Generating pfx.ps1:

# Generate private key
openssl genrsa -out private.key.pem 2048

# Generate self-signed certificate
openssl req -x509 -new -nodes `
  -key private.key.pem `
  -sha256 -days 365 `
  -subj "/C=ID/O=PT AMN Indonesia/CN=PT AMN Indonesia/emailAddress=development@amn.co.id" `
  -out certificate.crt.pem

# Copy certificate as CA chain
Copy-Item .\certificate.crt.pem .\ca-chain.pem -Force

# Create .pfx file (akan diminta password)
openssl pkcs12 -export `
  -inkey private.key.pem `
  -in certificate.crt.pem `
  -out certificate.pfx

Output Files:

  • private.key.pem - Private key (JANGAN disebarkan!)
  • certificate.crt.pem - Public certificate
  • ca-chain.pem - Certificate chain
  • certificate.pfx - Certificate dengan private key (untuk signing)

2. Konfigurasi di appsettings.json

{
  "PdfSignCertificate": {
    "AMN": {
      "Path": "CERT/certificate.pfx",
      "Password": "your-certificate-password"
    }
  }
}

⚠️ Security Best Practice: Jangan hardcode password di appsettings.json untuk production! Gunakan:

  • Environment Variables
  • Azure Key Vault
  • AWS Secrets Manager
  • User Secrets (untuk development)

Dengan Environment Variable:

{
  "PdfSignCertificate": {
    "AMN": {
      "Path": "CERT/certificate.pfx",
      "Password": "${CERT_PASSWORD}"
    }
  }
}
# Set environment variable
# Windows
set CERT_PASSWORD=your-password

# Linux/Mac
export CERT_PASSWORD=your-password

3. Register Service di Program.cs

using AmnDigitalSignature;

var builder = WebApplication.CreateBuilder(args);

// Register PDF Signing service
builder.Services.UseSigningPdf(builder.Configuration);

var app = builder.Build();

4. Gunakan di Controller/Service

public class DocumentController : ControllerBase
{
    private readonly PdfSigning _pdfSigning;

    public DocumentController(PdfSigning pdfSigning)
    {
        _pdfSigning = pdfSigning;
    }

    [HttpPost("sign")]
    public async Task<IActionResult> SignDocument(string pdfPath, string userId)
    {
        try
        {
            // Configure signature options
            var signOptions = new SignOptions
            {
                QrCodeString = $"USER:{userId}|DOC:{Path.GetFileName(pdfPath)}|DATE:{DateTime.Now:yyyy-MM-dd HH:mm:ss}",
                Page = -1,          // -1 = last page, 1 = first page
                XPos = -150,        // Negative = from right edge
                YPos = -150,        // Negative = from bottom edge
                Size = 100,         // Size in points
                Location = "Jakarta, Indonesia",
                Reason = "Document Approval",
                IsOverwrite = false // false = create new file with incremental numbering (filename_signed.pdf, filename_signed_1.pdf, etc.)
            };

            // Sign the PDF with digital signature
            var signedPath = await _pdfSigning.SignPdfDocument(
                pdfPath,
                "AMN",  // Certificate config key
                signOptions
            );

            return Ok(new {
                success = true,
                signedFile = signedPath,
                message = "Document signed successfully"
            });
        }
        catch (SignPdfProblemException ex)
        {
            return BadRequest(new {
                success = false,
                error = ex.Message
            });
        }
    }
}

📖 API Reference

SignOptions Class

Property Type Default Description
QrCodeString string? null Konten QR code. Jika null, signature tanpa QR code
Page int -1 Nomor halaman untuk signature. -1 = halaman terakhir
XPos int 250 Posisi X dalam points. Negatif = dari kanan
YPos int 500 Posisi Y dalam points. Negatif = dari bawah
Size int 100 Ukuran signature field dalam points
IsOverwrite bool false true = overwrite file asli, false = buat file baru dengan incremental numbering
Location string? null Lokasi penandatanganan (e.g., "Jakarta")
Reason string? null Alasan signing (e.g., "Approval")

Notes:

  • 1 point = 1/72 inch
  • A4 page ≈ 595 x 842 points (portrait)
  • Negative position: XPos = -100 berarti 100 points dari kanan
  • File naming dengan IsOverwrite = false:
    • Pertama: filename_signed.pdf
    • Jika exists: filename_signed_1.pdf
    • Jika exists: filename_signed_2.pdf
    • Dan seterusnya dengan incremental numbering

Methods

SignPdfDocument(string filePath, string configKey, SignOptions? options = null)

Menandatangani PDF dengan digital signature dan QR code.

Parameters:

  • filePath: Full path ke file PDF yang akan ditandatangani
  • configKey: Key konfigurasi certificate di appsettings.json
  • options: Konfigurasi signature (optional)

Returns:

  • Task<string>: Full path ke file PDF yang sudah ditandatangani

Throws:

  • SignPdfProblemException: Jika terjadi error (file not found, invalid certificate, dll)

Example:

var outputPath = await _pdfSigning.SignPdfDocument(
    "C:/documents/contract.pdf",
    "AMN",
    new SignOptions {
        QrCodeString = "Verification Code: ABC123",
        Location = "Jakarta",
        Reason = "Contract Approval"
    }
);
GetCertificatePath(string configKey)

Mendapatkan path certificate dari konfigurasi.

Example:

var certPath = _pdfSigning.GetCertificatePath("AMN");
// Returns: "CERT/certificate.pfx"
GetCertificatePublicKey(string configKey)

⚠️ Deprecated - Method ini untuk backward compatibility dengan PEM format. Untuk digital signature dengan .pfx, informasi certificate otomatis diambil dari file .pfx.

🔒 Digital Signature vs Visual Signature

✅ Digital Signature (Implementasi Sekarang)

Cara Kerja:

  1. Menggunakan private key dari certificate (.pfx) untuk signing
  2. Membuat cryptographic hash dari seluruh dokumen PDF
  3. Adobe Acrobat dapat memverifikasi:
    • Dokumen ditandatangani oleh siapa (dari certificate)
    • Dokumen tidak dimodifikasi sejak ditandatangani
    • Waktu penandatanganan

Kelebihan:

  • ✅ Terdeteksi sebagai "Signed" di Adobe Acrobat/Reader
  • ✅ Menampilkan informasi certificate saat diklik
  • ✅ Proteksi kriptografis - dokumen tidak bisa dimodifikasi
  • ✅ Legal binding dan non-repudiation
  • ✅ QR code tampil sebagai visual signature

Kekurangan:

  • ⚠️ Memerlukan certificate dengan private key (.pfx format)
  • ⚠️ Self-signed certificate akan muncul warning "Unknown" di Adobe
  • ⚠️ Production perlu certificate dari trusted Certificate Authority

Status di Adobe Acrobat:

  • Self-signed: ⚠️ "Signature validity is UNKNOWN"
  • CA-signed: ✅ "Signed and all signatures are valid"

❌ Visual Signature Only (Legacy)

Hanya menambahkan gambar QR code tanpa cryptographic signature.

  • Tidak terdeteksi sebagai "Signed"
  • Dokumen masih bisa dimodifikasi
  • Tidak ada proteksi kriptografis

🔐 Development vs Production

Development (Self-Signed Certificate)

Cara Generate:

cd CERT
.\Generating-pfx.ps1
# Input password saat diminta

Konfigurasi:

{
  "PdfSignCertificate": {
    "AMN": {
      "Path": "CERT/certificate.pfx",
      "Password": "dev-password"
    }
  }
}

Status di Adobe:

  • ⚠️ Warning: "Signature validity is UNKNOWN"
  • ⚠️ "The signer's identity is unknown because it has not been included in your list of trusted certificates"

Use Case:

  • Development dan testing
  • Internal documents
  • Proof of concept

Production (CA-Signed Certificate)

Cara Mendapatkan:

  1. Beli certificate dari Certificate Authority:

    • DigiCert
    • GlobalSign
    • Sectigo (formerly Comodo)
    • IdenTrust
    • CA Lokal (e.g., PrivyID, VIDA)
  2. Request dengan informasi organisasi:

    • Company Name
    • Country
    • Email
    • Domain (jika web signing)
  3. Validasi organisasi oleh CA

  4. Download certificate dalam format .pfx atau .p12

Konfigurasi:

{
  "PdfSignCertificate": {
    "PRODUCTION": {
      "Path": "certificates/company-cert.pfx",
      "Password": "${CERT_PASSWORD}"  // dari environment variable
    }
  }
}

Status di Adobe:

  • ✅ Green checkmark: "Signed and all signatures are valid"
  • ✅ "The signer's identity is verified by a trusted authority"

Use Case:

  • Production environment
  • Legal documents
  • Contracts
  • Regulatory compliance

📁 Output File Behavior

Library ini memiliki 2 mode untuk menentukan output file:

Mode 1: Overwrite Original File (IsOverwrite = true)

var signOptions = new SignOptions
{
    IsOverwrite = true  // Akan menimpa file asli
};

await _pdfSigning.SignPdfDocument("document.pdf", "AMN", signOptions);
// Output: document.pdf (file asli ditimpa)

Behavior:

  • File asli akan dihapus dan diganti dengan file yang sudah ditandatangani
  • Tidak ada backup file asli
  • ⚠️ Warning: Pastikan Anda sudah backup file asli jika diperlukan

Mode 2: Create New File with Incremental Numbering (IsOverwrite = false)

var signOptions = new SignOptions
{
    IsOverwrite = false  // Akan membuat file baru (default)
};

await _pdfSigning.SignPdfDocument("document.pdf", "AMN", signOptions);

Behavior dengan Incremental Numbering:

  1. First signing attempt:

    • Input: document.pdf
    • Output: document_signed.pdf
  2. Second signing attempt (jika document_signed.pdf sudah ada):

    • Input: document.pdf
    • Output: document_signed_1.pdf
  3. Third signing attempt (jika document_signed_1.pdf sudah ada):

    • Input: document.pdf
    • Output: document_signed_2.pdf
  4. Dan seterusnya...

    • document_signed_3.pdf
    • document_signed_4.pdf
    • dll.

Keuntungan:

  • ✅ File asli tetap aman (tidak ditimpa)
  • ✅ Tidak ada konflik jika signing berulang kali
  • ✅ Semua versi signed document tersimpan
  • ✅ History signing tetap terjaga

Use Cases:

  • Development/Testing: Berguna untuk testing berulang tanpa overwrite
  • Multiple Signers: Setiap signer bisa menghasilkan file terpisah
  • Audit Trail: Menyimpan semua versi signing untuk audit

📝 Contoh QR Code Content

QR code dapat berisi informasi apapun. Berikut contoh format yang recommended:

Format 1: Simple Verification Code

var qrContent = $"DOC-{documentId}|{DateTime.Now:yyyyMMddHHmmss}";
// Output: "DOC-12345|20260105143022"

Format 2: Detailed Information

var qrContent = $"USER:{userId}|DOC:{documentId}|DATE:{DateTime.Now:yyyy-MM-dd HH:mm:ss}|HASH:{documentHash}";
// Output: "USER:john.doe|DOC:12345|DATE:2026-01-05 14:30:22|HASH:abc123..."

Format 3: URL untuk Verification

var qrContent = $"https://verify.company.com/document/{documentId}";
// Output: "https://verify.company.com/document/12345"

Format 4: JSON (untuk data kompleks)

var qrData = new {
    documentId = 12345,
    userId = "john.doe",
    timestamp = DateTime.Now,
    department = "Finance"
};
var qrContent = JsonSerializer.Serialize(qrData);

✅ Validasi yang Diterapkan

Library melakukan validasi otomatis:

  1. Certificate Validation:

    • Config key harus ada di appsettings.json
    • File certificate (.pfx) harus exists
    • Certificate harus memiliki private key
    • Password certificate harus benar
  2. PDF File Validation:

    • File PDF harus exists
    • Hanya file dengan extension .pdf
  3. Page Number Validation:

    • Nomor halaman tidak boleh melebihi total halaman PDF
    • Page -1 otomatis ke halaman terakhir
  4. Position & Size Validation:

    • Signature field tidak boleh keluar dari batas halaman
    • Auto-adjustment dengan margin 10 points
    • Max size 90% dari dimensi halaman terkecil

🧪 Testing

Project dilengkapi dengan test suite di AmnDigitalSignature.Test:

cd AmnDigitalSignature.Test
dotnet run

Tests Available:

  • ✅ Config key not found
  • ✅ Certificate file not found
  • ✅ PDF file not found
  • ✅ Invalid file extension (non-PDF)
  • ✅ Page number exceeds available pages
  • ✅ Positive case: successful signing

🛠️ Troubleshooting

"Certificate does not contain a private key"

Problem: Certificate tidak memiliki private key untuk signing.

Solution:

  • Pastikan menggunakan file .pfx atau .p12, bukan .pem
  • Re-generate certificate dengan script PowerShell
  • Pastikan saat generate tidak ada error

"Signature validity is UNKNOWN" di Adobe

Problem: Self-signed certificate tidak di-trust oleh Adobe.

Solution untuk Development:

  1. Buka Adobe Acrobat
  2. Edit → Preferences → Signatures → Identities & Trusted Certificates
  3. Trusted Certificates → Import certificate (certificate.crt.pem)
  4. Check "Use this certificate as a trusted root"

Solution untuk Production:

  • Gunakan certificate dari Certificate Authority yang trusted

"Password is incorrect"

Problem: Password certificate salah.

Solution:

  • Pastikan password di appsettings.json sama dengan saat generate certificate
  • Jika lupa, re-generate certificate baru

"File is being used by another process"

Problem: PDF file sedang dibuka di aplikasi lain.

Solution:

  • Tutup Adobe Acrobat atau PDF reader lain
  • Gunakan IsOverwrite = false untuk create file baru

📦 Dependencies

<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
<PackageReference Include="PdfSharp" Version="6.2.3" />
<PackageReference Include="QRCoder" Version="1.7.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.*" Version="8.0.x" />

🗺️ Roadmap

  • Digital signature dengan X.509 certificate
  • QR code sebagai visual appearance
  • Self-signed certificate support
  • Auto-positioning dan validation
  • Certificate information display
  • Incremental file numbering untuk prevent overwrite
  • Timestamp server support (RFC 3161)
  • Multiple signatures pada satu dokumen
  • Signature verification API
  • PDF/A compliance
  • Long-term validation (LTV)

📄 License

[Your License Here]

🤝 Support

Untuk pertanyaan, bug report, atau feature request:

  • Create issue di repository
  • Email: development@amn.co.id

📚 References

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 274 1/14/2026