C#를 사용하여 PDF 문서에 디지털 서명 추가
Source: Dev.to
디지털 서명은 최신 문서 준수 및 데이터 보안을 위해 필수적입니다.
엔터프라이즈 수준의 .NET 개발에서는 계약 관리, 공식 문서 발행, 기밀 파일 배포와 같은 시나리오에 대해 **C#**를 사용한 PDF 디지털 서명 기능 구현이 높은 우선순위 요구사항입니다.
Free Spire.PDF for .NET은 가볍고 무료인 PDF 처리 라이브러리로, Adobe Acrobat과 같은 서드파티 소프트웨어 없이도 PDF 디지털 서명을 원활하게 통합할 수 있게 해줍니다.
1️⃣ 설치
Install-Package FreeSpire.PDF
NuGet 명령은 종속성을 자동으로 해결하고 라이브러리를 .NET 프로젝트에 통합합니다.
2️⃣ 인증서 요구 사항
| 환경 | .pfx 인증서를 얻는 방법 |
|---|---|
| 테스트 | OpenSSL 또는 Windows 인증서 관리자 를 사용하여 자체 서명 인증서를 생성합니다 (기능 테스트에만 적합). |
| 프로덕션 | 현지 규정을 준수하는 신뢰할 수 있는 인증 기관(CA) 에서 발급한 인증서를 사용합니다. |
인증서에는 공개 키와 개인 키가 모두 포함되어야 합니다.
인증서 로드
핵심 클래스는 System.Security.Cryptography.X509Certificates.X509Certificate2 입니다.
3️⃣ 서명 워크플로
PdfOrdinarySignatureMaker (Free Spire.PDF)는 표준 디지털 서명을 처리합니다.
선형 워크플로는 다음과 같습니다:
- PDF 문서 로드
- X.509 인증서 파싱
- 서명 메이커 초기화
- 서명 설정 구성
- 서명 실행
- 저장 및 리소스 해제
우리는 먼저 기본 보이지 않는 서명을 구현하고, 이후 텍스트와 이미지 요소가 포함된 사용자 정의 가능한 보이는 서명으로 확장할 것입니다.
4️⃣ 기본 보이지 않는 서명 (프로덕션 등급)
using Spire.Pdf;
using Spire.Pdf.Interactive.DigitalSignatures;
using System.Security.Cryptography.X509Certificates;
using System.IO;
namespace DigitallySignPdf
{
class Program
{
static void Main(string[] args)
{
// Define file paths (use relative paths for better portability)
string inputPdfPath = "Input.pdf";
string outputPdfPath = "Signed_Invisible.pdf";
string certPath = "certificate.pfx";
string certPassword = "abc123";
try
{
// Validate input files exist to avoid runtime exceptions
if (!File.Exists(inputPdfPath) || !File.Exists(certPath))
{
Console.WriteLine("Error: Input PDF or certificate file not found.");
return;
}
// 1. Initialize PdfDocument and load the target PDF
using (PdfDocument doc = new PdfDocument())
{
doc.LoadFromFile(inputPdfPath);
// 2. Load X.509 certificate with secure key storage flags
// Key flags explanation:
// - MachineKeySet: Suitable for server‑side deployment (shared key store)
// - EphemeralKeySet: Prevents key persistence, enhances security for temporary signing tasks
X509Certificate2 cert = new X509Certificate2(
certPath,
certPassword,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet
);
// 3. Initialize signature maker and bind the PDF document
PdfOrdinarySignatureMaker signatureMaker = new PdfOrdinarySignatureMaker(doc, cert);
// 4. Execute signature with a unique identifier (for later verification)
signatureMaker.MakeSignature("Enterprise_Signature_001");
// 5. Save the signed PDF (using 'using' statement auto‑releases resources)
doc.SaveToFile(outputPdfPath);
Console.WriteLine($"Success! Invisible signature added. Output file: {outputPdfPath}");
}
}
catch (CryptographicException ex)
{
Console.WriteLine($"Certificate error: {ex.Message} (Check password or certificate validity)");
}
catch (IOException ex)
{
Console.WriteLine($"File operation error: {ex.Message} (Ensure the PDF is not open in another program)");
}
catch (Exception ex)
{
Console.WriteLine($"Signature failed: {ex.Message}");
}
}
}
}
Note: 보이지 않는 서명은 최종 사용자에게 시각적인 확인을 제공하지 않습니다.
5️⃣ 고급 가시 서명 (맞춤형)
아래 예제는 텍스트, 이미지 및 레이아웃을 사용자 정의할 수 있는 가시 서명 필드를 추가합니다—계약서 및 공식 문서에 이상적입니다.
using Spire.Pdf;
using Spire.Pdf.Interactive.DigitalSignatures;
using System;
using System.Drawing;
using System.Security.Cryptography.X509Certificates;
using System.IO;
namespace PdfDigitalSignatureDemo
{
class AdvancedVisibleSignatureDemo
{
static void Main(string[] args)
{
string inputPdf = "Input.pdf";
string outputPdf = "Signed_Visible.pdf";
string certPath = "certificate.pfx";
string certPwd = "your_secure_password";
string signatureImagePath = "signature_stamp.png";
try
{
using (PdfDocument doc = new PdfDocument())
{
doc.LoadFromFile(inputPdf);
X509Certificate2
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
}
코드 스니펫은 원본 내용과 일치하도록 의도적으로 미완성 상태로 남겨두었습니다.
6️⃣ 요약 및 팁
| 주제 | 팁 |
|---|---|
| 인증서 처리 | 서버 환경에서는 MachineKeySet + EphemeralKeySet 사용을 권장합니다; 운영 환경에서는 절대로 비밀번호를 하드코딩하지 마세요. |
| 오류 처리 | CryptographicException, IOException, 그리고 일반 Exception을 잡아 명확한 진단 정보를 제공하세요. |
| 파일 경로 | 상대 경로나 설정 파일을 사용하여 환경 간 이식성을 향상시키세요. |
| 가시 서명 | 서명 사각형을 조정하고, 텍스트(PdfSignatureAppearance)를 추가하며, 이미지를 삽입하여 전문적인 모습을 구현하세요. |
| 테스트 | Adobe Acrobat Reader 또는 디지털 서명을 지원하는 PDF 뷰어로 서명을 검증하세요. |
7️⃣ 다음 단계
- Complete the visible‑signature demo – 사각형 위치 지정, 외관 설정 및 이미지 로딩을 추가합니다.
- Integrate with your CI/CD pipeline – 배포 전에 생성된 PDF에 서명을 자동화합니다.
- Implement verification logic –
PdfSignatureValidator(또는 서드‑파티 검증기)를 사용하여 수신 측에서 서명 무결성을 확인합니다.
Happy signing! 🚀
코드 예제 – 보이는 PDF 서명 추가
try
{
// Load the certificate (use secure storage flags)
var cert = new X509Certificate2(
certPath,
certPwd,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet);
// Initialise the signer and obtain the signature object
PdfOrdinarySignatureMaker signer = new PdfOrdinarySignatureMaker(doc, cert);
PdfSignature signature = signer.Signature;
// Configure signature metadata (visible in PDF signature properties)
signature.Name = "ACME Corporation Document Signing";
signature.ContactInfo = "compliance@acme.com";
signature.Location = "USA";
signature.Reason = "Confirm document content compliance and authenticity";
signature.Date = DateTime.Now; // Auto‑set signature timestamp
// Create and customise the signature appearance
PdfSignatureAppearance appearance = new PdfSignatureAppearance(signature)
{
NameLabel = "Signer: ",
ContactInfoLabel = "Contact: ",
LocationLabel = "Location: ",
ReasonLabel = "Reason: "
};
// Add a signature image (PNG/JPG). Resize if needed.
if (File.Exists(signatureImagePath))
{
PdfImage signatureImage = PdfImage.FromFile(signatureImagePath);
appearance.SignatureImage = signatureImage;
// Layout mode: Image + Text details
// Alternatives: SignImageOnly / SignDetailOnly
appearance.GraphicMode = GraphicMode.SignImageAndSignDetail;
}
else
{
Console.WriteLine("Signature image not found; using text‑only appearance.");
appearance.GraphicMode = GraphicMode.SignDetailOnly;
}
// Attach the appearance to the signer
signer.SignatureAppearance = appearance;
// Choose the target page (e.g., the last page of the PDF)
PdfPageBase targetPage = doc.Pages[doc.Pages.Count - 1];
// Set signature position (x, y, width, height) – adjust for your layout
const float x = 54f; // Left margin (72 pt = 1 in)
const float y = 330f; // Bottom margin
const float width = 280f;
const float height = 90f;
// Execute the visible signature
signer.MakeSignature(
"ACME_Visible_Signature_001",
targetPage,
x, y, width, height,
appearance);
// Save the signed PDF
doc.SaveToFile(outputPdf);
Console.WriteLine($"Visible signature added successfully! Output: {outputPdf}");
}
catch (Exception ex)
{
Console.WriteLine($"Advanced signature failed: {ex.Message}");
}
핵심 구성 요소 용어집 & 실용 팁
| 클래스 / 매개변수 | 핵심 기능 | 사용 시 주의사항 |
|---|---|---|
| PdfOrdinarySignatureMaker | 핵심 서명 엔진; PDF와 인증서를 결합한 뒤 서명을 실행합니다 | using 문을 사용하거나 Dispose()를 호출하여 리소스를 즉시 해제하세요. |
| X509Certificate2 | .pfx 인증서를 파싱하고 공개/개인 키 쌍을 추출합니다 | 항상 보안 키‑스토리지 플래그를 제공하고, 서버에서는 DefaultKeySet 사용을 피하세요. |
| X509KeyStorageFlags | 인증서가 저장되는 위치와 키의 수명 주기를 제어합니다 | • MachineKeySet – 서버‑측 배포용 • EphemeralKeySet – 일시적인, 메모리 내 키 |
| PdfSignatureAppearance | 보이는 서명 스타일(라벨, 이미지, 레이아웃)을 구성합니다 | GraphicMode를 조정하여 이미지 전용, 텍스트 전용, 또는 결합 레이아웃으로 전환합니다. |
| GraphicMode | 보이는 서명 레이아웃을 정의합니다 | 옵션: SignImageOnly, SignDetailOnly, SignImageAndSignDetail |
| MakeSignature() | 서명 프로세스를 트리거합니다 | 사후 서명 검증을 돕기 위해 고유한 서명 이름을 전달하세요. |
보안 모범 사례
- 비밀번호를 절대 하드코딩하지 마세요. 인증서 비밀번호를 안전한 금고(Azure Key Vault, AWS Secrets Manager) 또는 암호화된 구성 파일에 저장하세요.
- 인증서 접근을 제한하세요. .pfx 파일에 필요한 최소 파일 시스템 권한만 부여하세요.
- 앱 풀 아이덴티티. IIS에서 애플리케이션 풀 아이덴티티(예:
IIS AppPool\YourApp)에 인증서 저장소에 대한 읽기 권한을 부여하세요. - 서버에서는
UserKeySet사용을 피하세요. 비대화형 환경에서 권한 문제가 발생합니다.
일반 오류 시나리오 및 해결 방법
| 오류 유형 | 근본 원인 | 해결책 |
|---|---|---|
| CryptographicException | 잘못된 인증서 비밀번호 또는 권한 부족 | 비밀번호를 확인하고 X509KeyStorageFlags를 다시 점검한 뒤, 애플리케이션 풀에서 인증서를 읽을 수 있는지 확인하십시오. |
| IOException | PDF 파일이 다른 프로세스에 의해 잠겨 있음 | 서명하기 전에 PDF를 열어 놓은 모든 리더/편집기를 닫으십시오. |
| Invisible Signature Not Detected | 서명 이름 충돌 또는 PDF 저장이 불완전함 | 매번 고유한 서명 이름을 사용하고, 폐기하기 전에 SaveToFile()이 호출되었는지 확인하십시오. |
Validation Checklist
- Desktop readers: Adobe Acrobat DC, Foxit Reader
- Web browsers: Chrome PDF Viewer, Microsoft Edge PDF Reader
- Mobile: Adobe Acrobat Mobile, WPS Mobile
서명된 PDF를 위 환경에서 테스트하여 서명이 인식되고 올바르게 표시되는지 확인하십시오.
Why Choose Spire.PDF for .NET?
- 경량 및 손쉬운 통합 API – 무거운 서드파티 종속성이 없습니다.
PdfOrdinarySignatureMaker를 통한 보이지 않는 서명 및 보이는 서명에 대한 완전 지원.- 포괄적인 문서 및 예제 – 보안, 권한, 배포 등 일반적인 기업 문제점을 해결합니다.
문서 관리 시스템, 계약 서명 플랫폼, 혹은 기밀 파일 배포 도구를 구축하든, 이 접근 방식은 비용 효율적이며 고성능, 프로덕션 준비가 된 PDF 디지털 서명 솔루션을 제공합니다.