AMN.PdfSignLibrary
1.0.0
dotnet add package AMN.PdfSignLibrary --version 1.0.0
NuGet\Install-Package AMN.PdfSignLibrary -Version 1.0.0
<PackageReference Include="AMN.PdfSignLibrary" Version="1.0.0" />
<PackageVersion Include="AMN.PdfSignLibrary" Version="1.0.0" />
<PackageReference Include="AMN.PdfSignLibrary" />
paket add AMN.PdfSignLibrary --version 1.0.0
#r "nuget: AMN.PdfSignLibrary, 1.0.0"
#:package AMN.PdfSignLibrary@1.0.0
#addin nuget:?package=AMN.PdfSignLibrary&version=1.0.0
#tool nuget:?package=AMN.PdfSignLibrary&version=1.0.0
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 certificateca-chain.pem- Certificate chaincertificate.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 = -100berarti 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
- Pertama:
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 ditandatanganiconfigKey: Key konfigurasi certificate di appsettings.jsonoptions: 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:
- Menggunakan private key dari certificate (.pfx) untuk signing
- Membuat cryptographic hash dari seluruh dokumen PDF
- 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:
Beli certificate dari Certificate Authority:
- DigiCert
- GlobalSign
- Sectigo (formerly Comodo)
- IdenTrust
- CA Lokal (e.g., PrivyID, VIDA)
Request dengan informasi organisasi:
- Company Name
- Country
- Domain (jika web signing)
Validasi organisasi oleh CA
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:
First signing attempt:
- Input:
document.pdf - Output:
document_signed.pdf
- Input:
Second signing attempt (jika
document_signed.pdfsudah ada):- Input:
document.pdf - Output:
document_signed_1.pdf
- Input:
Third signing attempt (jika
document_signed_1.pdfsudah ada):- Input:
document.pdf - Output:
document_signed_2.pdf
- Input:
Dan seterusnya...
document_signed_3.pdfdocument_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:
Certificate Validation:
- Config key harus ada di appsettings.json
- File certificate (.pfx) harus exists
- Certificate harus memiliki private key
- Password certificate harus benar
PDF File Validation:
- File PDF harus exists
- Hanya file dengan extension
.pdf
Page Number Validation:
- Nomor halaman tidak boleh melebihi total halaman PDF
- Page -1 otomatis ke halaman terakhir
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
.pfxatau.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:
- Buka Adobe Acrobat
- Edit → Preferences → Signatures → Identities & Trusted Certificates
- Trusted Certificates → Import certificate (
certificate.crt.pem) - 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.jsonsama 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 = falseuntuk 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 | Versions 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. |
-
net7.0
- BouncyCastle.Cryptography (>= 2.6.2)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- PdfSharp (>= 6.2.3)
- QRCoder (>= 1.7.0)
- SixLabors.ImageSharp (>= 3.1.12)
- System.Drawing.Common (>= 9.0.0)
- System.Security.Cryptography.Pkcs (>= 9.0.0)
-
net8.0
- BouncyCastle.Cryptography (>= 2.6.2)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- PdfSharp (>= 6.2.3)
- QRCoder (>= 1.7.0)
- SixLabors.ImageSharp (>= 3.1.12)
- System.Drawing.Common (>= 9.0.0)
- System.Security.Cryptography.Pkcs (>= 9.0.0)
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 |