이 페이지에는 자동 번역된 텍스트가 포함될 수 있습니다.

C# 및 VB.NET에서 USB 토큰과 HSM 장치로 PDF에 서명하는 방법

신뢰할 수 있는 인증 기관은 더 이상 문서 서명 인증서의 개인 키 내보내기를 허용하지 않습니다. 대신 개인 키는 USB 토큰, 스마트 카드 또는 클라우드 기반 HSM과 같은 보안 하드웨어에 저장해야 합니다.

Docotic.Pdf library를 사용하면 HSM 장치를 통해 .NET에서 PDF 문서에 서명할 수 있습니다. 이 문서에서는 Azure Key Vault, AWS KMS, PKCS#11 호환 하드웨어와의 통합을 설명합니다.

C# .NET PDF 라이브러리 다운로드 페이지에서 라이브러리와 무료 기간 제한 라이선스 키를 받을 수 있습니다.

외부 PDF 서명

필수 조건

이 문서의 코드 샘플은 자체 서명 인증서를 사용하여 PDF 문서에 서명합니다. 기본적으로 PDF 뷰어는 이러한 서명을 신뢰하지 않습니다. 자체 서명 인증서는 테스트에만 사용해야 하며, 프로덕션 환경에서는 사용하면 안 됩니다. 신뢰할 수 있는 PDF 서명을 생성하려면 제3자 인증 기관이 발급한 문서 서명 인증서를 사용해야 합니다.

올바른 인증서 선택

Adobe Approved Trust List (AATL)에 나열된 회사의 인증서를 우선적으로 사용하세요. Adobe Acrobat은 기본적으로 이러한 회사의 인증서를 신뢰하며 "Certificate validity is unknown" 경고를 표시하지 않습니다.

EU(eIDAS) 규격을 준수하는 문서를 만들려면 European Union Trusted Lists (EUTL)에 나열된 제공업체가 발급한 Qualified Certificates를 사용해야 합니다.

.NET에서 인증서를 사용하는 방법 학습

문서 서명 인증서를 주문하면 일반적으로 HSM 장치에 대한 액세스 권한을 받게 됩니다. 물리적인 USB 토큰일 수도 있고, 클라우드 기반 HSM 솔루션일 수도 있습니다.

그다음에는 이 HSM으로 데이터를 서명하는 방법을 익혀야 합니다. 모든 암호화 제공자가 서로 다르기 때문에 보통 이 부분이 가장 까다롭습니다. USB 토큰 또는 클라우드 API의 문서를 따라야 합니다.

이상적으로는 대응되는 .NET SDK를 찾아야 합니다. 최소한 네이티브 SDK를 찾아 .NET에서 interop으로 사용해야 합니다.

목표는 인증서를 사용해 임의의 바이트에 서명할 수 있는 C# 또는 VB.NET 코드를 얻는 것입니다.

외부 서명을 위한 PDF API

HSM 장치를 사용해 임의의 바이트에 서명할 수 있게 되면, Docotic.Pdf로 PDF 문서에 서명할 준비가 된 것입니다. 이를 위해 IPdfSigner 인터페이스를 구현하고 서명에 사용하세요.

IPdfSigner 인터페이스 구현

IPdfSigner는 두 멤버를 선언합니다. SignatureAlgorithm 속성은 서명에 사용되는 알고리즘을 반환합니다. 일반적으로 RSA 또는 ECDSA 알고리즘을 사용합니다.

Sign 메서드는 외부 서명 흐름의 핵심입니다. Docotic.Pdf는 PDF 문서 바이트의 다이제스트를 이 메서드에 전달합니다. 이 다이제스트를 HSM 장치로 서명하고 디지털 서명의 바이트를 반환해야 합니다. 여기서는 PKCS#7 객체를 반환하면 안 됩니다. 암호학적 서명만 생성해서 반환하세요.

Docotic.Pdf는 각 서명마다 Sign 메서드를 두 번 호출합니다. 첫 번째 호출은 서명에 필요한 공간을 계산하기 위한 것입니다. 두 번째 호출은 실제로 문서에 서명하기 위한 것입니다.

외부 서명을 위한 PdfSigningOptions 만들기

IPdfSigner 구현으로 PDF 문서에 서명하려면 PdfSigningOptions(IPdfSigner, X509Certificate2[]) 생성자를 사용하세요. 인증서 체인을 가져와 두 번째 인수로 전달합니다. 최소 하나의 인증서가 필요하며, 서명 인증서가 먼저 와야 합니다.

마지막으로 확인할 요소는 PdfSigningOptions.DigestAlgorithm 속성입니다. 이 속성의 값은 IPdfSigner.SignatureAlgorithmIPdfSigner.Sign 구현과 일치해야 합니다.

일반적인 시나리오에서 외부 서명으로 PDF에 서명하는 방법을 살펴보겠습니다.

C#에서 PKCS#11 드라이버를 사용해 PDF 문서에 서명

암호화 USB 토큰 또는 스마트 카드에 PKCS#11 드라이버가 함께 제공되는 경우, .NET에서 Pkcs11Interop.X509Store 또는 Pkcs11Interop 라이브러리를 사용할 수 있습니다. Pkcs11Interop은 Atos CardOS 스마트 카드, YubiKey PIV, SmartCard-HSM, SafeNet ProtectServer HSM 및 기타 HSM과 호환됩니다.

PKCS#11 드라이버를 사용하여 PDF 문서에 서명 코드 샘플은 USB 토큰 또는 스마트 카드로 PDF 문서에 서명하는 방법을 보여줍니다. 다음 줄은 사용자 환경에 맞게 수정해야 합니다.

string pkcsLibraryPath = @"C:\Program Files\SoftHSM2\lib\softhsm2-x64.dll";
ulong slotId = 1743347971;
string alias = "YOUR-ALIAS";
string certLabel = "YOUR-CERTIFICATE";
string pin = "YOUR-PIN";
var digestAlgorithm = PdfDigestAlgorithm.Sha512;

샘플 구성에서는 USB 토큰의 에뮬레이터로 SoftHSM2를 사용합니다. 실제 애플리케이션에서는 SoftHSM2가 필요하지 않습니다. 대신 USB 토큰과 함께 제공되는 드라이버를 사용하세요.

Azure Key Vault를 사용해 PDF 문서에 서명

Microsoft Azure Key Vault는 인증서와 키를 안전하게 저장합니다. 다음 준비가 필요합니다.

  1. Azure 계정을 만듭니다.
  2. Azure 사용자를 만듭니다.
  3. Key Vault를 만듭니다.
  4. 선택한 Azure 사용자에게 Key Vault 접근을 허용합니다.
  5. 문서 서명 인증서를 Key Vault에 추가합니다. 통합 인증 기관의 인증서를 가져오기할 수 있습니다.

그다음 .NET 서명에 Azure.Security.KeyVault.Keys NuGet 패키지를 사용합니다. Azure Key Vault의 키로 서명하는 C# 샘플 코드:

var vaultUrl = new Uri("https://YOUR-VAULT.vault.azure.net/");
var credentials = new DefaultAzureCredential();

var keyClient = new KeyClient(vaultUrl, credentials);
KeyVaultKey key = keyClient.GetKey("YOUR-KEY");
var cryptoClient = new CryptographyClient(key.Id, credentials);
byte[] messageToSign = ..;
byte[] signature = cryptoClient.SignData(SignatureAlgorithm.RS512, messageToSign).Signature;

관련된 IPdfSigner 구현은 다음과 같을 수 있습니다.

using AzureSignatureAlgorithm = SignatureAlgorithm;

class AzureSigner : IPdfSigner
{
    private readonly CryptographyClient m_client;
    private readonly AzureSignatureAlgorithm m_signingAlgorithm;

    public AzureSigner(CryptographyClient client, AzureSignatureAlgorithm signingAlgorithm)
    {
        m_client = client;
        m_signingAlgorithm = signingAlgorithm;
    }

    public PdfSignatureAlgorithm SignatureAlgorithm
    {
        get
        {
            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS256 ||
                m_signingAlgorithm == AzureSignatureAlgorithm.RS384 ||
                m_signingAlgorithm == AzureSignatureAlgorithm.RS512)
                return PdfSignatureAlgorithm.Rsa;

            throw new NotSupportedException($"Unsupported {nameof(SignatureAlgorithm)} value: {m_signingAlgorithm}");
        }
    }

    public PdfDigestAlgorithm DigestAlgorithm
    {
        get
        {
            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS256)
                return PdfDigestAlgorithm.Sha256;

            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS384)
                return PdfDigestAlgorithm.Sha384;

            if (m_signingAlgorithm == AzureSignatureAlgorithm.RS512)
                return PdfDigestAlgorithm.Sha512;

            throw new NotSupportedException($"Unsupported {nameof(SignatureAlgorithm)} value: {m_signingAlgorithm}");
        }
    }

    public byte[] Sign(byte[] message)
    {
        return m_client.SignData(m_signingAlgorithm, message).Signature;
    }
}

마지막으로 이 AzureSigner 클래스를 사용해 PDF 문서에 서명합니다.

CryptographyClient cryptoClient = ..;
using X509Certificate2 cert = ..;
var signer = new AzureSigner(cryptoClient, SignatureAlgorithm.RS512);

using var pdf = new PdfDocument();

PdfPage page = pdf.Pages[0];
PdfSignatureField field = page.AddSignatureField(50, 50, 200, 200);

var options = new PdfSigningOptions(signer, [cert])
{
    DigestAlgorithm = signer.DigestAlgorithm,
    Field = field,
};

pdf.SignAndSave(options, ..);

GitHub에서 전체 Azure Key Vault를 사용하여 PDF 문서에 서명 코드 샘플을 확인하세요.

AWS KMS를 사용해 PDF 문서에 서명

AWS Key Management Service (KMS)는 암호화 키를 위한 또 다른 안전한 저장소입니다. 준비 과정은 Azure Key Vault와 동일합니다.

  1. AWS 계정을 만듭니다.
  2. IAM 사용자를 만듭니다.
  3. 서명 키를 추가합니다.
  4. 선택한 IAM 사용자에게 키 접근을 허용합니다.

AWS에서는 .NET에서 AWSSDK.KeyManagementService NuGet 패키지를 사용해야 합니다. PDF 서명용 샘플 코드는 Azure 시나리오와 매우 유사합니다.

using var pdf = new PdfDocument();

PdfPage page = pdf.Pages[0];
PdfSignatureField field = page.AddSignatureField(50, 50, 200, 200);

var signingAlgorithm = SigningAlgorithmSpec.RSASSA_PSS_SHA_512;
var signer = new AwsSigner("arn:aws:kms:YOUR-id", signingAlgorithm);
var options = new PdfSigningOptions(signer, [cert])
{
    DigestAlgorithm = signer.DigestAlgorithm,
    Field = field,
};

pdf.SignAndSave(options, ..);


class AwsSigner : IPdfSigner
{
    private readonly string m_keyId;
    private readonly SigningAlgorithmSpec m_signingAlgorithm;

    public AwsSigner(string keyId, SigningAlgorithmSpec signingAlgorithm)
    {
        m_keyId = keyId;
        m_signingAlgorithm = signingAlgorithm;
    }

    public PdfSignatureAlgorithm SignatureAlgorithm
    {
        get
        {
            if (m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_256 ||
                m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_384 ||
                m_signingAlgorithm == SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_512)
                return PdfSignatureAlgorithm.Rsa;

            throw new NotSupportedException($"Unsupported {nameof(SigningAlgorithmSpec)} value: {m_signingAlgorithm}");
        }
    }

    public PdfDigestAlgorithm DigestAlgorithm
    {
        get
        {
            string alg = m_signingAlgorithm.Value;
            if (alg.EndsWith("256"))
                return PdfDigestAlgorithm.Sha256;

            if (alg.EndsWith("384"))
                return PdfDigestAlgorithm.Sha384;

            if (alg.EndsWith("512"))
                return PdfDigestAlgorithm.Sha512;

            throw new NotSupportedException($"Unsupported {nameof(SigningAlgorithmSpec)} value: {m_signingAlgorithm}");
        }
    }

    public byte[] Sign(byte[] message)
    {
        using var kmsClient = new AmazonKeyManagementServiceClient();
        using var stream = new MemoryStream(message);
        var signRequest = new SignRequest()
        {
            SigningAlgorithm = m_signingAlgorithm,
            KeyId = m_keyId,
            MessageType = MessageType.RAW,
            Message = stream,
        };
        SignResponse signResponse = kmsClient.SignAsync(signRequest).GetAwaiter().GetResult();
        return signResponse.Signature.ToArray();
    }
}

전체 AWS KMS를 사용하여 PDF 문서에 서명 코드 샘플도 GitHub에서 확인할 수 있습니다.

결론

Docotic.Pdf library를 활용해 USB 토큰, 스마트 카드 또는 클라우드 기반 HSM으로 PDF 문서에 서명하세요. 이를 위해 C# 또는 VB.NET 코드에서 IPdfSigner 인터페이스를 구현하면 됩니다.

GitHub에서 PDF의 디지털 서명용 코드 샘플을 다운로드하고 실행해 보세요. 추가 정보는 다음 관련 문서를 확인하세요.

자주 묻는 질문

USB 토큰 또는 스마트 카드로 PDF에 서명하려면 어떻게 해야 합니까?

Docotic.Pdf 및 Pkcs11Interop .NET 라이브러리를 사용하여 토큰 또는 스마트 카드로 PDF에 서명합니다. 자세한 내용은 C#에서 PKCS#11 드라이버를 사용해 PDF 문서에 서명 섹션을 참조하세요.

USB 토큰 또는 스마트 카드와 함께 SoftHSM을 사용하려면 어떻게 해야 합니까?

SoftHSM은 실제 HSM 장치를 시뮬레이션합니다. USB 토큰이나 스마트 카드가 있다면 SoftHSM이 필요하지 않습니다. 대신 HSM 장치와 함께 제공되는 PKCS#11 드라이버를 사용하세요.

외부 서명으로 PDF 문서에 서명하려면 어떻게 해야 합니까?

Azure Key Vault를 사용해 PDF 문서에 서명AWS KMS를 사용해 PDF 문서에 서명 섹션을 확인하세요. 다른 클라우드 기반 HSM을 사용한 외부 PDF 서명도 유사한 절차를 따릅니다.

.NET에서 Azure Key Vault를 사용하려면 어떻게 해야 합니까?

Azure.Security.KeyVault.Keys NuGet 패키지를 사용하세요. GitHub의 Azure Key Vault를 사용하여 PDF 문서에 서명 코드 샘플을 시도해 보세요.