如何在 C# 中讀取 Verified PDF 簽名中的簽署者憑證(X.509、eIDAS、憑證鏈)

This article was translated from English: Does it need improvement?
Translated
View the article in English

一份合約進入您的稽核流程。該合約已進行數位簽署,簽名有效,而您的合規團隊需要以下資訊:簽署者是誰、當日上午其憑證是否有效,以及簽發的 CA 是否與您的受信任清單相符?您可以提取原始簽名位元組、解析 DER 格式,並手動追蹤憑證鏈。 或者,您可以向 VerifiedSignature 物件查詢其 SignerCertificate,並將回傳的結果作為屬性讀取。

這正是 VerifiedSignature 上的 SignerCertificateCertificateChain 屬性所設計的目的。 從 PdfDocument.GetVerifiedSignatures() 傳回的資訊,會揭露簽署者的完整 X.509 身分、簽發的 CA、有效期限、SHA-256 拇指印、原始 DER 位元組,以及所有直至根憑證的中間憑證。 這正是您為符合 eIDAS 規範、憑證固定、稽核追蹤及信任鏈驗證所需之 API 介面。

本指南將帶您逐步了解三類憑證檢查:

  • 標準 X.509 詳細資訊:身分屬性、完整識別名稱、有效期範圍,以及用於 X509Certificate2 互通的原始 DER 位元組。
  • eIDAS 及受監管產業領域:SubjectSerialNumber 提取簽署者的國民身分證號碼,並讀取如 2.5.4.97 這類數值 OID 作為 eIDAS 組織識別碼。
  • 信任鏈驗證:SHA-256 拇指印錨定,並沿著從簽署者到根憑證授權機構的完整鏈路進行驗證。

立即開始 30 天試用,在您的驗證流程中測試憑證檢查功能。

NuGet 透過 NuGet 安裝

PM >  Install-Package IronPdf

請至 NuGet 查閱 https://www.nuget.org/packages/IronPdf 以快速安裝。該套件下載量已突破 1,000 萬次,正透過 C# 徹底改變 PDF 開發領域。 您亦可下載 DLL 檔案或 Windows 安裝程式

快速入門:從已簽署的 PDF 讀取簽署者憑證

在已載入的 PDF 上呼叫 GetVerifiedSignatures,並直接從結果中讀取簽署者的憑證屬性。

  1. using NuGet 套件管理員安裝 https://www.nuget.org/packages/IronPdf

    PM > Install-Package IronPdf
  2. 請複製並執行此程式碼片段。

    var sig = PdfDocument.FromFile("signed.pdf").GetVerifiedSignatures().First();
    var cert = sig.SignerCertificate;
    Console.WriteLine($"{cert?.CommonName} ({cert?.Organization}) valid until {cert?.NotAfter}");
  3. 部署至您的生產環境進行測試

    立即透過免費試用,在您的專案中開始使用 IronPDF

    arrow pointer


如何提取簽署者的國民身分證號或個人身分證號?

SubjectSerialNumber 屬性會從憑證的"主體 DN"中傳回 SERIALNUMBER OID 的值。 在受監管的產業(例如 eIDAS 合格憑證、銀行業、政府核發的數位身分識別)中,此欄位會填入簽署者的國民身分證號碼、稅務識別號碼或個人識別號碼。 這並非憑證本身的序號; 即 CertificateSerialNumber

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-serial-number.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("eidas-signed-invoice.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Subject DN SERIALNUMBER OID (national ID, tax ID, etc.)
    Console.WriteLine($"Signer Identity (SERIALNUMBER): {cert.SubjectSerialNumber ?? "not present"}");

    // Certificate's own serial from the issuing CA
    Console.WriteLine($"Certificate Serial (CA-issued):  {cert.CertificateSerialNumber}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("eidas-signed-invoice.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Subject DN SERIALNUMBER OID (national ID, tax ID, etc.)
    Console.WriteLine($"Signer Identity (SERIALNUMBER): {If(cert.SubjectSerialNumber, "not present")}")

    ' Certificate's own serial from the issuing CA
    Console.WriteLine($"Certificate Serial (CA-issued):  {cert.CertificateSerialNumber}")
Next
$vbLabelText   $csharpLabel

提示關於這兩者的區別,在此簡要說明:SubjectSerialNumber 會告訴您簽署了該文件(個人或組織識別碼,例如嵌入於主體 DN 中的國民身分證號或稅號)。 CertificateSerialNumber 會告訴您使用了哪張憑證; (這是簽發憑證授權機構為撤銷清單和稽核日誌所指派的唯一序列號。

如何存取便利性識別屬性?

SignerCertificateInfo 將最常使用的"主體 DN"欄位直接作為屬性公開,因此您無需自行解析完整的 DN 字串。

讀取簽署者的姓名與所屬機構

CountryEmail 這些屬性涵蓋了大多數身分識別工作流程最常首先使用的功能。

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-identity-common.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("signed-agreement.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Common Subject DN fields exposed directly as properties
    Console.WriteLine($"CommonName (CN):  {cert.CommonName}");
    Console.WriteLine($"Organization (O): {cert.Organization}");
    Console.WriteLine($"Country (C):      {cert.Country}");
    Console.WriteLine($"Email:            {cert.Email}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("signed-agreement.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Common Subject DN fields exposed directly as properties
    Console.WriteLine($"CommonName (CN):  {cert.CommonName}")
    Console.WriteLine($"Organization (O): {cert.Organization}")
    Console.WriteLine($"Country (C):      {cert.Country}")
    Console.WriteLine($"Email:            {cert.Email}")
Next
$vbLabelText   $csharpLabel

Email 屬性會先檢查主體 DN 是否包含 emailAddress OID。 若未存在,則會回退至"主體替代名稱"(Subject Alternative Name, SAN)rfc822Name 擴充名稱。 Country 屬性會傳回 ISO 3166-1 alpha-2 兩字母代碼(例如"DE"、"US"、"FR")。 若憑證中不存在對應的欄位,所有便利屬性皆會回傳 null

當您需要完整資訊時,請閱讀完整的 DN

針對稽核記錄、下游解析或合規報告,SubjectDNIssuerDN 會返回完整的"識別名稱"字串。

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-identity-full-dn.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("signed-agreement.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Full DN strings for audit logging or downstream parsing
    Console.WriteLine($"Full Subject DN: {cert.SubjectDN}");
    Console.WriteLine($"Full Issuer DN:  {cert.IssuerDN}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("signed-agreement.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Full DN strings for audit logging or downstream parsing
    Console.WriteLine($"Full Subject DN: {cert.SubjectDN}")
    Console.WriteLine($"Full Issuer DN:  {cert.IssuerDN}")
Next
$vbLabelText   $csharpLabel

如何擷取任意的主體與發行者欄位?

GetSubjectField(string)GetIssuerField(string) 方法皆可接受簡稱或數值 OID。 這些字元不區分大小寫,若字段不存在則會返回 null

支援的簡稱:CN, O, OU, C, L, ST, E, SERIALNUMBER, SURNAME, T, GIVENNAME, UID, ORGANIZATIONIDENTIFIER.

讀取任何主題領域(包括 eIDAS OID)

傳遞一個簡短名稱(例如 "OU")或一個數值 OID(例如 "2.5.4.97",即 eIDAS 組織識別碼),即可從主體 DN 中擷取任何欄位。

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-field-subject.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("eidas-contract.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Short-name lookups (case-insensitive)
    string? orgUnit = cert.GetSubjectField("OU");
    string? surname = cert.GetSubjectField("SURNAME");
    string? givenName = cert.GetSubjectField("GIVENNAME");
    string? title = cert.GetSubjectField("T");

    // Numeric OID lookup (eIDAS organizationIdentifier)
    string? orgId = cert.GetSubjectField("2.5.4.97");

    Console.WriteLine($"Name: {givenName} {surname} ({title}), OU: {orgUnit}");
    Console.WriteLine($"eIDAS Org ID (2.5.4.97): {orgId ?? "not present"}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("eidas-contract.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Short-name lookups (case-insensitive)
    Dim orgUnit As String = cert.GetSubjectField("OU")
    Dim surname As String = cert.GetSubjectField("SURNAME")
    Dim givenName As String = cert.GetSubjectField("GIVENNAME")
    Dim title As String = cert.GetSubjectField("T")

    ' Numeric OID lookup (eIDAS organizationIdentifier)
    Dim orgId As String = cert.GetSubjectField("2.5.4.97")

    Console.WriteLine($"Name: {givenName} {surname} ({title}), OU: {orgUnit}")
    Console.WriteLine($"eIDAS Org ID (2.5.4.97): {If(orgId, "not present")}")
Next
$vbLabelText   $csharpLabel

數值 OID 查詢對於 eIDAS 及受監管產業的憑證至關重要,此類憑證內嵌了組織識別碼、專業資格,或標準短名稱未涵蓋的特定管轄區欄位。

讀取發行者的欄位

GetIssuerField 針對簽發憑證授權機構(CA)的識別名稱,其運作方式相同。 這對於信任驗證、CA 報告,以及根據簽發機構對簽名進行分組非常有用。

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-field-issuer.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("eidas-contract.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // GetIssuerField works the same way as GetSubjectField
    string? issuerCN = cert.GetIssuerField("CN");
    string? issuerO = cert.GetIssuerField("O");
    string? issuerCountry = cert.GetIssuerField("C");

    Console.WriteLine($"Issued by: {issuerCN} ({issuerO}, {issuerCountry})");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("eidas-contract.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' GetIssuerField works the same way as GetSubjectField
    Dim issuerCN As String = cert.GetIssuerField("CN")
    Dim issuerO As String = cert.GetIssuerField("O")
    Dim issuerCountry As String = cert.GetIssuerField("C")

    Console.WriteLine($"Issued by: {issuerCN} ({issuerO}, {issuerCountry})")
Next
$vbLabelText   $csharpLabel

如何檢查憑證的有效性?

SignerCertificateInfo 提供兩個有效性檢查輔助函式:

  • IsExpiredDateTime.UtcNow > NotAfter,則返回 true
  • 若給定時間戳記落在 NotAfter 時間窗內,IsValidAt(DateTime) 將返回 true。 典型的應用情境是確認憑證在簽署當下是否有效。
:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-validity.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("archived-contract.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    Console.WriteLine($"Valid from:  {cert.NotBefore}");
    Console.WriteLine($"Valid until: {cert.NotAfter}");
    Console.WriteLine($"Expired now: {cert.IsExpired}");

    // Confirm certificate was valid when the document was signed
    if (sig.SigningDate.HasValue)
    {
        bool validAtSigning = cert.IsValidAt(sig.SigningDate.Value);
        Console.WriteLine($"Valid at signing time ({sig.SigningDate.Value}): {validAtSigning}");
    }
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("archived-contract.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    Console.WriteLine($"Valid from:  {cert.NotBefore}")
    Console.WriteLine($"Valid until: {cert.NotAfter}")
    Console.WriteLine($"Expired now: {cert.IsExpired}")

    ' Confirm certificate was valid when the document was signed
    If sig.SigningDate.HasValue Then
        Dim validAtSigning As Boolean = cert.IsValidAt(sig.SigningDate.Value)
        Console.WriteLine($"Valid at signing time ({sig.SigningDate.Value}): {validAtSigning}")
    End If
Next
$vbLabelText   $csharpLabel

提示IsExpired 會傳回 false(而非 true),當無法從憑證中提取有效期限時。 此輔助程式刻意採取保守策略;不會僅因缺少元資料就預設其已過期。 對於零信任工作流程,請將缺少有效期限的情況明確視為失敗案例。

針對稽核追蹤,請同時記錄 IsExpired 檢查(當前有效性)與 IsValidAt(signingDate) 檢查(簽署時的有效性)。 一張已過期的憑證,在文件簽署時可能仍屬有效; 這兩項數據點對於合規性而言都至關重要。

如何使用 SHA-256 拇指印進行憑證固定?

Sha256Thumbprint 屬性會傳回一個 64 字元的全大寫十六進位字串,此為 DER 編碼憑證的 SHA-256 雜湊值。 這是您將其與已知有效的簽署者憑證清單進行比對的值。

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-thumbprint-pinning.cs
using IronPdf;
using IronPdf.Signing.Inspection;
using System.Collections.Generic;

// Known-good signer thumbprints from a compliance database
var pinnedThumbprints = new HashSet<string>
{
    "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2",
    "F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5"
};

var pdf = PdfDocument.FromFile("submitted-form.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Pin against trusted thumbprint list
    bool trusted = pinnedThumbprints.Contains(cert.Sha256Thumbprint);
    Console.WriteLine($"Signer: {cert.CommonName}, Thumbprint: {cert.Sha256Thumbprint}");
    Console.WriteLine($"Pinned: {trusted}");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection
Imports System.Collections.Generic

' Known-good signer thumbprints from a compliance database
Dim pinnedThumbprints As New HashSet(Of String) From {
    "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2",
    "F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5D4C3B2A1F6E5"
}

Dim pdf = PdfDocument.FromFile("submitted-form.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Pin against trusted thumbprint list
    Dim trusted As Boolean = pinnedThumbprints.Contains(cert.Sha256Thumbprint)
    Console.WriteLine($"Signer: {cert.CommonName}, Thumbprint: {cert.Sha256Thumbprint}")
    Console.WriteLine($"Pinned: {trusted}")
Next
$vbLabelText   $csharpLabel

如何從原始憑證資料建構 X509Certificate2?

RawData 屬性會將 DER 編碼的憑證以 byte[] 格式傳回。 每次呼叫都會返回一個防禦性副本,因此內部資料絕不會透過參照方式暴露。 您可以利用此功能建立 System.Security.Cryptography.X509Certificates.X509Certificate2,以實現與 .NET 原生加密 API 的互通(包括鏈建立、政策驗證及 CRL 檢查)。

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-x509-interop.cs
using IronPdf;
using IronPdf.Signing.Inspection;
using System.Security.Cryptography.X509Certificates;

var pdf = PdfDocument.FromFile("signed-report.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    var cert = sig.SignerCertificate;
    if (cert is null) continue;

    // Construct X509Certificate2 from defensive DER copy
    byte[] derBytes = cert.RawData;
    var x509 = new X509Certificate2(derBytes);

    Console.WriteLine($"X509 Subject:    {x509.Subject}");
    Console.WriteLine($"X509 Issuer:     {x509.Issuer}");
    Console.WriteLine($"X509 Serial:     {x509.SerialNumber}");
    Console.WriteLine($"X509 Algorithm:  {x509.SignatureAlgorithm.FriendlyName}");
    Console.WriteLine($"X509 Key Size:   {x509.PublicKey.Key.KeySize} bits");
}
Imports IronPdf
Imports IronPdf.Signing.Inspection
Imports System.Security.Cryptography.X509Certificates

Dim pdf = PdfDocument.FromFile("signed-report.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Dim cert = sig.SignerCertificate
    If cert Is Nothing Then Continue For

    ' Construct X509Certificate2 from defensive DER copy
    Dim derBytes As Byte() = cert.RawData
    Dim x509 = New X509Certificate2(derBytes)

    Console.WriteLine($"X509 Subject:    {x509.Subject}")
    Console.WriteLine($"X509 Issuer:     {x509.Issuer}")
    Console.WriteLine($"X509 Serial:     {x509.SerialNumber}")
    Console.WriteLine($"X509 Algorithm:  {x509.SignatureAlgorithm.FriendlyName}")
    Console.WriteLine($"X509 Key Size:   {x509.PublicKey.Key.KeySize} bits")
Next
$vbLabelText   $csharpLabel

請注意每次存取 RawData 都會返回一份新的防禦性副本。 內部位元組陣列絕不會透過參考暴露,因此修改回傳的位元組內容不會影響後續的呼叫。)}]

此互通路徑讓您能將 IronPDF 的簽名驗證功能整合至現有的 .NET 憑證驗證流程中,例如 X509Chain.Build()、透過 CRL/OCSP 進行的撤銷檢查,或是自訂的 X509CertificateValidator 實作。

如何遍歷憑證鏈?

CertificateChain 屬性在 VerifiedSignature 上會傳回一個 IReadOnlyList<SignerCertificateInfo>,其順序為簽署者 → 中間憑證機構(s) → 根憑證機構。 索引 0 始終代表簽署者憑證(與 SignerCertificate 為同一物件)。 此清單絕不會為空; 若無法提取憑證鏈,則該憑證僅包含簽署者憑證。

:path=/static-assets/pdf/content-code-examples/how-to/verify-pdf-signatures-chain-walking.cs
using IronPdf;
using IronPdf.Signing.Inspection;

var pdf = PdfDocument.FromFile("enterprise-signed.pdf");

foreach (var sig in pdf.GetVerifiedSignatures())
{
    Console.WriteLine($"--- Signature: {sig.SignatureName} ---");
    Console.WriteLine($"Chain length: {sig.CertificateChain.Count}");

    // Walk from signer (index 0) through intermediates to root CA
    for (int i = 0; i < sig.CertificateChain.Count; i++)
    {
        var cert = sig.CertificateChain[i];
        string role = i == 0 ? "Signer"
                    : i == sig.CertificateChain.Count - 1 ? "Root CA"
                    : $"Intermediate CA ({i})";

        Console.WriteLine($"  [{role}]");
        Console.WriteLine($"    Subject:     {cert.SubjectDN}");
        Console.WriteLine($"    Issuer:      {cert.IssuerDN}");
        Console.WriteLine($"    Serial:      {cert.CertificateSerialNumber}");
        Console.WriteLine($"    Valid:       {cert.NotBefore} to {cert.NotAfter}");
        Console.WriteLine($"    Expired:     {cert.IsExpired}");
        Console.WriteLine($"    Thumbprint:  {cert.Sha256Thumbprint}");
    }
}
Imports IronPdf
Imports IronPdf.Signing.Inspection

Dim pdf = PdfDocument.FromFile("enterprise-signed.pdf")

For Each sig In pdf.GetVerifiedSignatures()
    Console.WriteLine($"--- Signature: {sig.SignatureName} ---")
    Console.WriteLine($"Chain length: {sig.CertificateChain.Count}")

    ' Walk from signer (index 0) through intermediates to root CA
    For i As Integer = 0 To sig.CertificateChain.Count - 1
        Dim cert = sig.CertificateChain(i)
        Dim role As String = If(i = 0, "Signer", If(i = sig.CertificateChain.Count - 1, "Root CA", $"Intermediate CA ({i})"))

        Console.WriteLine($"  [{role}]")
        Console.WriteLine($"    Subject:     {cert.SubjectDN}")
        Console.WriteLine($"    Issuer:      {cert.IssuerDN}")
        Console.WriteLine($"    Serial:      {cert.CertificateSerialNumber}")
        Console.WriteLine($"    Valid:       {cert.NotBefore} to {cert.NotAfter}")
        Console.WriteLine($"    Expired:     {cert.IsExpired}")
        Console.WriteLine($"    Thumbprint:  {cert.Sha256Thumbprint}")
    Next
Next
$vbLabelText   $csharpLabel

請注意鏈中的每個 SignerCertificateInfo 皆會公開與 SignerCertificate 相同的屬性:識別字段、有效性、拇指印、GetSubjectField() 以及 GetIssuerField()。 您可以驗證每個中間憑證的到期日期、將根憑證的拇指印與您的受信任根憑證儲存庫進行比對,並記錄完整的憑證鏈以供合規稽核使用。)]

後續步驟

SignerCertificateCertificateChain 讓您無需離開 IronPDF API 介面,即可進行完整的 X.509 檢查。 接下來,自然會進一步深入探討這些組件所整合的 IronPDF 相關功能。

針對工作流程中的簽署環節,數位簽名操作指南涵蓋了憑證建立、元資料以及增量簽署等內容。 針對高安全性環境中的 HSM 簽名,HSM 簽名指南涵蓋了 PKCS#11 整合。 需要可直接使用的程式碼片段嗎? 數位簽章程式碼範例即為其提供。

準備好在您自己的簽名 PDF 上試試看嗎? 立即開始 30 天試用,或查看授權方案

Curtis Chau
技術撰稿人

Curtis Chau 擁有卡爾頓大學(Carleton University)的電腦科學學士學位,專精於前端開發,並精通 Node.js、TypeScript、JavaScript 及 React。他熱衷於打造直觀且美觀的用戶介面,喜歡運用現代框架,並創建結構完善、視覺上吸引人的手冊。

除了開發工作之外,Curtis 對物聯網(IoT)抱有濃厚興趣,致力於探索整合硬體與軟體的創新方法。閒暇時,他喜歡玩遊戲和開發 Discord 機器人,將對科技的熱愛與創意相結合。

準備開始了嗎?
Nuget 下載 18,918,602 | 版本: 2026.5 just released
Still Scrolling Icon

還在往下捲動嗎?

想要快速確認成果嗎? PM > Install-Package IronPdf
執行範例 觀看您的 HTML 轉為 PDF。