Przejdź do treści stopki
PORóWNANIA PRODUKTóW
Jak dodać podpis cyfrowy do pliku PDF w języku C# przy użyciu biblioteki ITextSharp

Dodaj podpis cyfrowy do PDF w C# używając Itextsharp

Full Comparison

Looking for a detailed feature-by-feature breakdown? See how IronPDF stacks up against Itext on pricing, HTML support, and licensing.

View Full Comparison

W naszym szybko zmieniającym się świecie papierowe dokumenty są szybko zastępowane dokumentami elektronicznymi. Czy to podpisywanie umowy, akceptacja faktury, czy składanie formularza rządowego, dokumentacja cyfrowa stała się standardem. Jednakże, to udogodnienie niesie ze sobą nowe wyzwanie: zapewnienie autentyczności i integralności dokumentów cyfrowych.

Podpisy elektroniczne oferują rozwiązanie. Więcej niż tylko bazgroły na ekranie dotykowym, podpisy cyfrowe używają technik kryptograficznych do weryfikacji tożsamości podpisującego i zabezpieczają, że treść dokumentu pozostaje niezmieniona. Dla programistów C#, integracja takiego poziomu bezpieczeństwa do przepływów pracy z PDF jest teraz łatwiejsza niż kiedykolwiek, zwłaszcza z narzędziami takimi jak IronPDF i iTextSharp. W tym artykule poprowadzimy Cię przez proces podpisywania cyfrowego plików PDF, porównamy biblioteki, przedstawimy najlepsze praktyki, i pomożemy Ci wybrać odpowiednie rozwiązanie do Twojego następnego projektu.

Zrozumienie podpisów cyfrowych

Podpis cyfrowy to technika kryptograficzna używana do weryfikacji autentyczności i integralności cyfrowej wiadomości lub dokumentu. W odróżnieniu od zwykłego podpisu obrazkowego lub wpisanego nazwiska, podpis cyfrowy używa klucza prywatnego do zaszyfrowania haszu dokumentu. Ten zaszyfrowany hasz może być następnie zweryfikowany przez każdego przy użyciu klucza publicznego podpisującego.

Dlaczego jest to ważne? Ponieważ zapewnia dwie rzeczy:

  1. Autentyfikacja – Podpis weryfikuje dokumenty PDF, które pochodzą od wspomnianego nadawcy.

  2. Integralność – Dokument nie został zmieniony od momentu podpisania. Nawet najmniejsza zmiana unieważnia podpis.

Podpisy cyfrowe są prawnie wiążące w wielu jurysdykcjach i są istotne w branżach takich jak finanse, ochrona zdrowia, prawo i sektor publiczny.

Dlaczego używać podpisów cyfrowych w dokumentach PDF?

PDF to standardowy format do dystrybucji profesjonalnych dokumentów, od umów prawnych po raporty oficjalne. Dodawanie podpisów cyfrowych do dokumentów PDF pełni kilka kluczowych funkcji:

  • Legalność i zgodność: Podpisy cyfrowe są zgodne z przepisami takimi jak eIDAS (Europa), ESIGN (USA) i inne, dzięki czemu są prawnie uznawane.

  • Bezpieczeństwo: Podpisane dokumenty nie mogą być zmieniane bez naruszenia podpisu, co chroni przed manipulacją lub oszustwem.

  • Efektywność: Nie ma potrzeby drukować, podpisywać ani skanować. Oszczędzaj czas i upraszczaj przepływy pracy dzięki bezpiecznym zatwierdzeniom cyfrowym.

  • Zaufanie: Klienci i partnerzy mogą z pewnością weryfikować pochodzenie i integralność dokumentów.

Krótko mówiąc, podpisy cyfrowe wnoszą zaufanie i efektywność do Twoich przepływów pracy z dokumentami.

Porównanie iTextSharp i IronPDF

Przy implementacji podpisów cyfrowych w C# często wyróżniają się dwie biblioteki: iTextSharp i IronPDF. Obie są zaawansowanymi narzędziami, ale są przeznaczone dla różnych typów programistów i wymagań projektowych. Zbadajmy, jak się one porównują w rzeczywistych zastosowaniach.

iTextSharp: Moc złożoności

iTextSharp to dobrze znana nazwa w świecie manipulacji PDF. Jest częścią szerszego ekosystemu iText 7, oferując szerokie wsparcie dla operacji na niskim poziomie PDF, w tym cyfrowego podpisywania kryptograficznego.

Programiści, którzy potrzebują szczegółowej kontroli nad wyglądem podpisu, algorytmami haszującymi, łańcuchami certyfikatów i niestandardowymi przepływami weryfikacyjnymi, uznają iTextSharp za bardzo zdolny. Jest wysoce rozszerzalny i zaprojektowany z myślą o skomplikowanych potrzebach przedsiębiorstw.

Jednakże, ta elastyczność ma swoją cenę. Krzywa uczenia się jest stroma. Proste zadania, takie jak dodanie widocznego podpisu, często wymagają wielu klas, strumieni i kroków konfiguracji. Dla nowych użytkowników może to być przytłaczające.

Dodatkowo, iTextSharp jest licencjonowany na zasadach AGPL, które wymagają, aby Twoja aplikacja była open-source, chyba że zakupisz komercyjną licencję - co stanowi problem dla wielu projektów zamkniętych lub zastrzeżonych.

IronPDF: Prostota spotyka profesjonalizm

IronPDF, w przeciwieństwie do tego, stosuje nowoczesne podejście zorientowane na programistów. Jego API jest zaprojektowane do obsługi powszechnych zadań PDF - takich jak podpisy cyfrowe, generowanie, scalanie i edytowanie - przy minimalnej konfiguracji, co czyni ją potężną biblioteką PDF dla projektów frameworku .NET.

Na przykład, podpisanie PDF w IronPDF nie wymaga bezpośredniej pracy ze strumieniami czy ustawieniami kryptograficznymi. Wystarczy wczytać PDF, wywołać SignPdf(), i przekazać swój certyfikat. Obsługuje on nawet dodatkowe metadane, takie jak lokalizacja podpisu, powód i dane kontaktowe - wszystko to w jednym wywołaniu metody.

Kolejną kluczową zaletą jest licencjonowanie. IronPDF oferuje przyjazną dla biznesu licencję bez ograniczeń AGPL, co jest idealne dla profesjonalnych i korporacyjnych aplikacji. I chociaż jest to produkt płatny, hojna darmowa wersja próbna umożliwia łatwą ocenę przed podjęciem decyzji.

Podsumowanie obok siebie

Funkcja iTextSharp IronPDF
Łatwość użycia Stroma krzywa uczenia się Przyjazne dla początkujących, minimalny kod
Licencja AGPL (lub płatna licencja komercyjna) Licencja komercyjna bez wymogu open-source
Dostosowanie podpisu Wysoce dostosowywalne z kontrolą kryptograficzną Uproszczone API z opcjonalnymi polami metadanych
Dokumentacja Szczegółowy, ale złożony Jasne przykłady z dokumentacją skupioną na programistach
Najlepsze dla Aplikacje korporacyjne z głębokim dostosowaniem Zespoły potrzebujące szybkiej implementacji i wsparcia

Jak zacząć z iTextSharp i IronPDF

Zanim zanurzysz się w implementacje podpisów cyfrowych, ważne jest, aby zrozumieć, jak rozpocząć pracę z każdą z bibliotek. Niezależnie od tego, czy budujesz rozwiązanie dla korporacji, czy szybkie narzędzie wewnętrzne, odpowiednie ustawienie może zrobić ogromną różnicę.

Konfiguracja iTextSharp

iTextSharp to port .NET potężnej biblioteki iText PDF opartej na Javie. Aby rozpocząć, musisz go zainstalować przez NuGet i dodać odpowiednie przestrzenie nazw w swoim projekcie.

Instalacja

Możesz łatwo zainstalować bibliotekę iTextSharp do swojego projektu za pośrednictwem konsoli Menedżera pakietów NuGet. Wystarczy uruchomić następujące polecenie:

Install-Package iTextSharp

Instalacja iTextSharp

Ta łatwa instalacja zapewnia szybką implementację tej biblioteki w Twoim projekcie C#.

Podstawowa konfiguracja

Po zainstalowaniu, możesz zacząć używać przestrzeni nazw iTextSharp w swoim projekcie:

using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
Imports iTextSharp.text.pdf
Imports iTextSharp.text.pdf.security
$vbLabelText   $csharpLabel

Pamiętaj, że iTextSharp jest modułowy. Jeśli planujesz używać zaawansowanych funkcji kryptograficznych lub znakowania czasem, prawdopodobnie będziesz potrzebować dodatkowych pakietów jak BouncyCastle.Cryptography. Można je zainstalować podobnie jak iTextSharp, wystarczy uruchomić linię:

Install-Package BouncyCastle.Cryptography

Rzeczy, na które należy uważać

  • Licencjonowanie: Licencja AGPL wymaga, aby każde oprogramowanie korzystające z iTextSharp było open-source, chyba że kupisz licencję komercyjną.

  • Zależności: Operacje kryptograficzne często wymagają BouncyCastle do obsługi certyfikatów.

  • Krzywa uczenia się: Nawet podstawowe podpisywanie wymaga zrozumienia PdfSigner, IExternalSignature, oraz różnych dostawców kryptograficznych.

Jeśli komfortowo konfigurujesz te elementy konstrukcyjne i potrzebujesz pełnej kontroli nad procesem podpisywania (np. ustawieniu wyglądu, poziomu weryfikacji czy serwerów znakowania czasem), iTextSharp to solidny wybór.

Konfiguracja IronPDF

IronPDF to komercyjna biblioteka PDF stworzona z myślą o efektywności programistów. Została zaprojektowana dla programistów .NET, którzy chcą generować, edytować i podpisywać PDFy z minimalnym wysiłkiem. IronPDF oferuje znacznie płynniejszą ścieżkę wdrożenia, zwłaszcza dla tych, którzy cenią czyste API i szybkie rezultaty.

Instalacja

Zainstaluj najnowszy pakiet IronPDF przez NuGet:

Install-Package IronPdf

Instalacja IronPDF przez konsolę NuGet

Lub użyj interfejsu CLI platformy .NET:

Install-Package IronPdf

Podstawowa konfiguracja

Zacznij od importu głównej przestrzeni nazw IronPDF:

using IronPdf;
using IronPdf;
Imports IronPdf
$vbLabelText   $csharpLabel

I to wszystko — jesteś gotowy, by wczytać plik PDF i zacząć dodawać podpisy cyfrowe.

IronPDF zarządza wszystkim wewnętrznie: ładowaniem certyfikatów, pozycjonowaniem podpisów wizualnych, metadanymi i finalnym eksportem. Nie musisz ręcznie zarządzać strumieniami PDF ani algorytmami kryptograficznymi, co jest ogromną zaletą przy szybkim rozwoju.

Kluczowe zalety dla początkujących

  • Wszystko w jednym: Brak dodatkowych zależności lub bibliotek kryptograficznych.

  • Brak problemów z AGPL: IronPDF oferuje licencję wieczystą i hojny okres testowy.

  • Wizualne renderowanie: IronPDF renderuje PDF dokładnie tak, jak wyglądałby po wydrukowaniu, co czyni go idealnym dla umów i oficjalnych dokumentów.

Krok po kroku: Dodawanie podpisu cyfrowego

1. Przygotuj swój certyfikat

Potrzebujesz pliku certyfikatu cyfrowego .pfx i hasła. Są one używane do generowania podpisu cyfrowego. Możesz uzyskać certyfikat od zaufanego Urzędu Certyfikacji (CA) lub wygenerować go do użytku wewnętrznego przy użyciu takich narzędzi jak OpenSSL.

2. Podpis PDF za pomocą iTextSharp i BouncyCastle

Dołącz niezbędne przestrzenie nazw

Najpierw upewnijmy się, że mamy poprawne instrukcje using na górze kodu, aby uzyskać dostęp do różnych klas i metod wymaganych do podpisu elektronicznego PDF za pomocą iTextSharp.

using System;
using System.IO;
using System.Linq;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using System;
using System.IO;
using System.Linq;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
Imports System
Imports System.IO
Imports System.Linq
Imports iTextSharp.text.pdf
Imports iTextSharp.text.pdf.security
Imports Org.BouncyCastle.Crypto
Imports Org.BouncyCastle.Pkcs
$vbLabelText   $csharpLabel

Zdefiniuj ścieżkę wejściową PDF i wczytaj ją do PdfReader

Następnie określamy ścieżkę do istniejącego PDF i ładujemy ją do PdfReader. Przypiszemy również kilka zmiennych typu string, które będą używane później w kodzie.

// Path to the unsigned PDF you want to sign
string filename = "example.pdf";

// Load the existing PDF into a reader
PdfReader pdfReader = new PdfReader(filename);

string reason = "Digital Signature Reason";
string location = "Digital Signature Location";
// Path to the unsigned PDF you want to sign
string filename = "example.pdf";

// Load the existing PDF into a reader
PdfReader pdfReader = new PdfReader(filename);

string reason = "Digital Signature Reason";
string location = "Digital Signature Location";
' Path to the unsigned PDF you want to sign
Dim filename As String = "example.pdf"

' Load the existing PDF into a reader
Dim pdfReader As New PdfReader(filename)

Dim reason As String = "Digital Signature Reason"
Dim location As String = "Digital Signature Location"
$vbLabelText   $csharpLabel

Zdefiniuj ścieżkę certyfikatu i hasło

Następnie wskazujemy na plik certyfikatu .pfx i podajemy hasło do jego zabezpieczenia.

// Path to your .pfx certificate file (must contain private key)
string pfxFilePath = "certificate-file.pfx";

// Password for the certificate (make sure to protect this securely!)
string pfxPassword = "Password";
// Path to your .pfx certificate file (must contain private key)
string pfxFilePath = "certificate-file.pfx";

// Password for the certificate (make sure to protect this securely!)
string pfxPassword = "Password";
' Path to your .pfx certificate file (must contain private key)
Dim pfxFilePath As String = "certificate-file.pfx"

' Password for the certificate (make sure to protect this securely!)
Dim pfxPassword As String = "Password"
$vbLabelText   $csharpLabel

Wczytaj certyfikat .PFX przy użyciu Pkcs12Store

Używamy BouncyCastle do załadowania certyfikatu i klucza prywatnego do bezpiecznego magazynu.

// Initialize a new PKCS#12 key store (used for handling the PFX certificate)
Pkcs12StoreBuilder pkcs12StoreBuilder = new Pkcs12StoreBuilder();
Pkcs12Store pfxKeyStore = pkcs12StoreBuilder.Build();

// Load the certificate and private key from the PFX file
using (FileStream pfxStream = new FileStream(pfxFilePath, FileMode.Open, FileAccess.Read))
{
    // Load into the key store using the provided password
    pfxKeyStore.Load(pfxStream, pfxPassword.ToCharArray());
}
// Initialize a new PKCS#12 key store (used for handling the PFX certificate)
Pkcs12StoreBuilder pkcs12StoreBuilder = new Pkcs12StoreBuilder();
Pkcs12Store pfxKeyStore = pkcs12StoreBuilder.Build();

// Load the certificate and private key from the PFX file
using (FileStream pfxStream = new FileStream(pfxFilePath, FileMode.Open, FileAccess.Read))
{
    // Load into the key store using the provided password
    pfxKeyStore.Load(pfxStream, pfxPassword.ToCharArray());
}
' Initialize a new PKCS#12 key store (used for handling the PFX certificate)
Dim pkcs12StoreBuilder As New Pkcs12StoreBuilder()
Dim pfxKeyStore As Pkcs12Store = pkcs12StoreBuilder.Build()

' Load the certificate and private key from the PFX file
Using pfxStream As New FileStream(pfxFilePath, FileMode.Open, FileAccess.Read)
	' Load into the key store using the provided password
	pfxKeyStore.Load(pfxStream, pfxPassword.ToCharArray())
End Using
$vbLabelText   $csharpLabel

Przygotuj PdfStamper do dodania podpisu

PdfStamper pozwala nam zastosować podpis cyfrowy, jednocześnie zachowując oryginalną treść.

// Create a PdfStamper that enables signing and appends the signature to the document
PdfStamper pdfStamper = PdfStamper.CreateSignature(
    pdfReader,
    new FileStream("MyPDF_Signed.pdf", FileMode.Create), // Output path
    '\0',                                                // PDF version (unchanged)
    null,                                                // Temp file path (optional)
    true                                                 // Append mode (preserves original content)
);
// Create a PdfStamper that enables signing and appends the signature to the document
PdfStamper pdfStamper = PdfStamper.CreateSignature(
    pdfReader,
    new FileStream("MyPDF_Signed.pdf", FileMode.Create), // Output path
    '\0',                                                // PDF version (unchanged)
    null,                                                // Temp file path (optional)
    true                                                 // Append mode (preserves original content)
);
Imports Microsoft.VisualBasic

' Create a PdfStamper that enables signing and appends the signature to the document
Dim pdfStamper As PdfStamper = PdfStamper.CreateSignature(pdfReader, New FileStream("MyPDF_Signed.pdf", FileMode.Create), ControlChars.NullChar, Nothing, True)
$vbLabelText   $csharpLabel

Dostosuj wygląd podpisu

Teraz definiujemy, jak i gdzie podpis będzie się wizualnie pojawiał w dokumencie.

// Access the signature appearance settings
PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;

// Add optional metadata (shows up in PDF signature details)
signatureAppearance.Reason = reason;
signatureAppearance.Location = location;

// Position the visible signature on the page (x, y, width, height in points)
float x = 360;
float y = 130;
signatureAppearance.Acro6Layers = false;              // Use compact signature appearance
signatureAppearance.Layer4Text = PdfSignatureAppearance.QuestionMark; // Custom label text
signatureAppearance.SetVisibleSignature(
    new iTextSharp.text.Rectangle(x, y, x + 150, y + 50), // Rectangle position
    1,                                                    // Page number
    "signature"                                           // Field name
);
// Access the signature appearance settings
PdfSignatureAppearance signatureAppearance = pdfStamper.SignatureAppearance;

// Add optional metadata (shows up in PDF signature details)
signatureAppearance.Reason = reason;
signatureAppearance.Location = location;

// Position the visible signature on the page (x, y, width, height in points)
float x = 360;
float y = 130;
signatureAppearance.Acro6Layers = false;              // Use compact signature appearance
signatureAppearance.Layer4Text = PdfSignatureAppearance.QuestionMark; // Custom label text
signatureAppearance.SetVisibleSignature(
    new iTextSharp.text.Rectangle(x, y, x + 150, y + 50), // Rectangle position
    1,                                                    // Page number
    "signature"                                           // Field name
);
' Access the signature appearance settings
Dim signatureAppearance As PdfSignatureAppearance = pdfStamper.SignatureAppearance

' Add optional metadata (shows up in PDF signature details)
signatureAppearance.Reason = reason
signatureAppearance.Location = location

' Position the visible signature on the page (x, y, width, height in points)
Dim x As Single = 360
Dim y As Single = 130
signatureAppearance.Acro6Layers = False ' Use compact signature appearance
signatureAppearance.Layer4Text = PdfSignatureAppearance.QuestionMark ' Custom label text
signatureAppearance.SetVisibleSignature(New iTextSharp.text.Rectangle(x, y, x + 150, y + 50), 1, "signature")
$vbLabelText   $csharpLabel

Wyciągnij klucz prywatny i podpisz PDF

Odzyskujemy alias (nazwę) wpisu certyfikatu, które zawiera klucz prywatny. Jeśli istnieje alias, przystępujemy do generowania i osadzenia podpisu cyfrowego przy użyciu SHA-256.

// Find the first alias in the PFX that has a private key entry
string alias = pfxKeyStore.Aliases.Cast<string>().FirstOrDefault(
    entryAlias => pfxKeyStore.IsKeyEntry(entryAlias)
);

// Ensure a valid alias (certificate) was found
if (alias != null)
{
    // Retrieve the private key for signing
    ICipherParameters privateKey = pfxKeyStore.GetKey(alias).Key;

    // Create a signer using SHA-256 and the private key
    IExternalSignature pks = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256);

    // Perform the digital signing operation using CMS format
    MakeSignature.SignDetached(
        signatureAppearance,     // Signature appearance
        pks,                     // External signature handler
        new Org.BouncyCastle.X509.X509Certificate[] {
            pfxKeyStore.GetCertificate(alias).Certificate
        },                       // Certificate chain (basic single-cert example)
        null, null, null,        // Optional CRL, OCSP, TSA
        0,                       // Estimated size for the signature (0 = auto)
        CryptoStandard.CMS       // Signature standard (CMS vs CAdES)
    );
}
else
{
    Console.WriteLine("Private key not found in the PFX certificate.");
}
// Find the first alias in the PFX that has a private key entry
string alias = pfxKeyStore.Aliases.Cast<string>().FirstOrDefault(
    entryAlias => pfxKeyStore.IsKeyEntry(entryAlias)
);

// Ensure a valid alias (certificate) was found
if (alias != null)
{
    // Retrieve the private key for signing
    ICipherParameters privateKey = pfxKeyStore.GetKey(alias).Key;

    // Create a signer using SHA-256 and the private key
    IExternalSignature pks = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA256);

    // Perform the digital signing operation using CMS format
    MakeSignature.SignDetached(
        signatureAppearance,     // Signature appearance
        pks,                     // External signature handler
        new Org.BouncyCastle.X509.X509Certificate[] {
            pfxKeyStore.GetCertificate(alias).Certificate
        },                       // Certificate chain (basic single-cert example)
        null, null, null,        // Optional CRL, OCSP, TSA
        0,                       // Estimated size for the signature (0 = auto)
        CryptoStandard.CMS       // Signature standard (CMS vs CAdES)
    );
}
else
{
    Console.WriteLine("Private key not found in the PFX certificate.");
}
' Find the first alias in the PFX that has a private key entry
Dim [alias] As String = pfxKeyStore.Aliases.Cast(Of String)().FirstOrDefault(Function(entryAlias) pfxKeyStore.IsKeyEntry(entryAlias))

' Ensure a valid alias (certificate) was found
If [alias] IsNot Nothing Then
	' Retrieve the private key for signing
	Dim privateKey As ICipherParameters = pfxKeyStore.GetKey([alias]).Key

	' Create a signer using SHA-256 and the private key
	Dim pks As IExternalSignature = New PrivateKeySignature(privateKey, DigestAlgorithms.SHA256)

	' Perform the digital signing operation using CMS format
	MakeSignature.SignDetached(signatureAppearance, pks, New Org.BouncyCastle.X509.X509Certificate() { pfxKeyStore.GetCertificate([alias]).Certificate }, Nothing, Nothing, Nothing, 0, CryptoStandard.CMS)
Else
	Console.WriteLine("Private key not found in the PFX certificate.")
End If
$vbLabelText   $csharpLabel

Finalizuj dokument

Na koniec zamykamy stemper, aby ukończyć proces podpisywania i zapisujemy podpisany PDF na dysku.

// Close the stamper to save and finalize the signed PDF
pdfStamper.Close();
// Close the stamper to save and finalize the signed PDF
pdfStamper.Close();
' Close the stamper to save and finalize the signed PDF
pdfStamper.Close()
$vbLabelText   $csharpLabel

Wynik

PDF podpisany przy użyciu iTextSharp

Bootstrap - Formularz konfiguracji bezpieczeństwa

Profesjonalne zabezpieczenie PDF wymaga intuicyjnych interfejsów konfiguracji. Ten przykład Bootstrap 5 demonstruje zdolność IronPDF do renderowania wieloetapowych formularzy konfiguracji zabezpieczeń z walidacją stanów i śledzeniem postępów.

using IronPdf;

var renderer = new ChromePdfRenderer();

string securityConfigForm = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
    <style>
        .step-indicator { display: flex; justify-content: space-between; margin-bottom: 30px; }
        .step { flex: 1; text-align: center; position: relative; }
        .step-number { width: 40px; height: 40px; border-radius: 50%; background: #e9ecef;
            display: inline-flex; align-items: center; justify-content: center; font-weight: 700; }
        .step.completed .step-number { background: #198754; color: white; }
        .step.active .step-number { background: #0d6efd; color: white; }
        @media print { .form-section { page-break-inside: avoid; } }
    </style>
</head>
<body class='bg-light'>
    <div class='container py-4'>
        <div class='row justify-content-center'>
            <div class='col-lg-8'>
                <h2 class='text-center mb-4'>PDF Security Configuration</h2>

                <div class='step-indicator mb-4'>
                    <div class='step completed'>
                        <div class='step-number'>✓</div>
                        <div class='small mt-2'>Certificate</div>
                    </div>
                    <div class='step completed'>
                        <div class='step-number'>✓</div>
                        <div class='small mt-2'>Signature</div>
                    </div>
                    <div class='step active'>
                        <div class='step-number'>3</div>
                        <div class='small mt-2'>Encryption</div>
                    </div>
                    <div class='step'>
                        <div class='step-number'>4</div>
                        <div class='small mt-2'>Finalize</div>
                    </div>
                </div>

                <div class='card shadow-sm form-section'>
                    <div class='card-header bg-primary text-white'>
                        <h5 class='mb-0'>Step 3: Encryption & Permissions</h5>
                    </div>
                    <div class='card-body'>
                        <div class='mb-3'>
                            <label class='form-label'><strong>Encryption Level</strong></label>
                            <select class='form-select'>
                                <option>AES 128-bit</option>
                                <option selected>AES 256-bit (Recommended)</option>
                                <option>RC4 128-bit (Legacy)</option>
                            </select>
                            <small class='text-muted'>AES-256 provides enterprise-grade security</small>
                        </div>

                        <div class='mb-3'>
                            <label class='form-label'><strong>Document Permissions</strong></label>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowPrint' checked>
                                <label class='form-check-label' for='allowPrint'>Allow Printing</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowCopy'>
                                <label class='form-check-label' for='allowCopy'>Allow Content Copying</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowModify'>
                                <label class='form-check-label' for='allowModify'>Allow Document Modification</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowAnnotate' checked>
                                <label class='form-check-label' for='allowAnnotate'>Allow Annotations</label>
                            </div>
                        </div>

                        <div class='mb-3'>
                            <label class='form-label'><strong>Password Protection</strong></label>
                            <input type='password' class='form-control mb-2' placeholder='Owner Password' value='••••••••'>
                            <input type='password' class='form-control' placeholder='User Password (Optional)'>
                            <small class='text-muted'>Owner password controls document permissions</small>
                        </div>

                        <div class='alert alert-success'>
                            <strong>✓ Configuration Valid</strong><br>
                            <small>Security settings meet compliance requirements</small>
                        </div>
                    </div>
                    <div class='card-footer'>
                        <div class='d-flex justify-content-between'>
                            <button class='btn btn-outline-secondary'>← Previous</button>
                            <button class='btn btn-primary'>Continue →</button>
                        </div>
                    </div>
                </div>

                <div class='card mt-3 shadow-sm'>
                    <div class='card-body'>
                        <h6 class='text-primary'>Security Comparison</h6>
                        <div class='row g-2'>
                            <div class='col-6'>
                                <div class='text-center p-2 bg-success text-white rounded'>
                                    <strong>IronPDF</strong><br>
                                    <small>AES-256 Native</small>
                                </div>
                            </div>
                            <div class='col-6'>
                                <div class='text-center p-2 bg-warning text-dark rounded'>
                                    <strong>iTextSharp</strong><br>
                                    <small>Complex Setup</small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>";

var pdf = renderer.RenderHtmlAsPdf(securityConfigForm);
pdf.SaveAs("security-configuration.pdf");
using IronPdf;

var renderer = new ChromePdfRenderer();

string securityConfigForm = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
    <style>
        .step-indicator { display: flex; justify-content: space-between; margin-bottom: 30px; }
        .step { flex: 1; text-align: center; position: relative; }
        .step-number { width: 40px; height: 40px; border-radius: 50%; background: #e9ecef;
            display: inline-flex; align-items: center; justify-content: center; font-weight: 700; }
        .step.completed .step-number { background: #198754; color: white; }
        .step.active .step-number { background: #0d6efd; color: white; }
        @media print { .form-section { page-break-inside: avoid; } }
    </style>
</head>
<body class='bg-light'>
    <div class='container py-4'>
        <div class='row justify-content-center'>
            <div class='col-lg-8'>
                <h2 class='text-center mb-4'>PDF Security Configuration</h2>

                <div class='step-indicator mb-4'>
                    <div class='step completed'>
                        <div class='step-number'>✓</div>
                        <div class='small mt-2'>Certificate</div>
                    </div>
                    <div class='step completed'>
                        <div class='step-number'>✓</div>
                        <div class='small mt-2'>Signature</div>
                    </div>
                    <div class='step active'>
                        <div class='step-number'>3</div>
                        <div class='small mt-2'>Encryption</div>
                    </div>
                    <div class='step'>
                        <div class='step-number'>4</div>
                        <div class='small mt-2'>Finalize</div>
                    </div>
                </div>

                <div class='card shadow-sm form-section'>
                    <div class='card-header bg-primary text-white'>
                        <h5 class='mb-0'>Step 3: Encryption & Permissions</h5>
                    </div>
                    <div class='card-body'>
                        <div class='mb-3'>
                            <label class='form-label'><strong>Encryption Level</strong></label>
                            <select class='form-select'>
                                <option>AES 128-bit</option>
                                <option selected>AES 256-bit (Recommended)</option>
                                <option>RC4 128-bit (Legacy)</option>
                            </select>
                            <small class='text-muted'>AES-256 provides enterprise-grade security</small>
                        </div>

                        <div class='mb-3'>
                            <label class='form-label'><strong>Document Permissions</strong></label>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowPrint' checked>
                                <label class='form-check-label' for='allowPrint'>Allow Printing</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowCopy'>
                                <label class='form-check-label' for='allowCopy'>Allow Content Copying</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowModify'>
                                <label class='form-check-label' for='allowModify'>Allow Document Modification</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowAnnotate' checked>
                                <label class='form-check-label' for='allowAnnotate'>Allow Annotations</label>
                            </div>
                        </div>

                        <div class='mb-3'>
                            <label class='form-label'><strong>Password Protection</strong></label>
                            <input type='password' class='form-control mb-2' placeholder='Owner Password' value='••••••••'>
                            <input type='password' class='form-control' placeholder='User Password (Optional)'>
                            <small class='text-muted'>Owner password controls document permissions</small>
                        </div>

                        <div class='alert alert-success'>
                            <strong>✓ Configuration Valid</strong><br>
                            <small>Security settings meet compliance requirements</small>
                        </div>
                    </div>
                    <div class='card-footer'>
                        <div class='d-flex justify-content-between'>
                            <button class='btn btn-outline-secondary'>← Previous</button>
                            <button class='btn btn-primary'>Continue →</button>
                        </div>
                    </div>
                </div>

                <div class='card mt-3 shadow-sm'>
                    <div class='card-body'>
                        <h6 class='text-primary'>Security Comparison</h6>
                        <div class='row g-2'>
                            <div class='col-6'>
                                <div class='text-center p-2 bg-success text-white rounded'>
                                    <strong>IronPDF</strong><br>
                                    <small>AES-256 Native</small>
                                </div>
                            </div>
                            <div class='col-6'>
                                <div class='text-center p-2 bg-warning text-dark rounded'>
                                    <strong>iTextSharp</strong><br>
                                    <small>Complex Setup</small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>";

var pdf = renderer.RenderHtmlAsPdf(securityConfigForm);
pdf.SaveAs("security-configuration.pdf");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

Dim securityConfigForm As String = "
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css' rel='stylesheet'>
    <style>
        .step-indicator { display: flex; justify-content: space-between; margin-bottom: 30px; }
        .step { flex: 1; text-align: center; position: relative; }
        .step-number { width: 40px; height: 40px; border-radius: 50%; background: #e9ecef;
            display: inline-flex; align-items: center; justify-content: center; font-weight: 700; }
        .step.completed .step-number { background: #198754; color: white; }
        .step.active .step-number { background: #0d6efd; color: white; }
        @media print { .form-section { page-break-inside: avoid; } }
    </style>
</head>
<body class='bg-light'>
    <div class='container py-4'>
        <div class='row justify-content-center'>
            <div class='col-lg-8'>
                <h2 class='text-center mb-4'>PDF Security Configuration</h2>

                <div class='step-indicator mb-4'>
                    <div class='step completed'>
                        <div class='step-number'>✓</div>
                        <div class='small mt-2'>Certificate</div>
                    </div>
                    <div class='step completed'>
                        <div class='step-number'>✓</div>
                        <div class='small mt-2'>Signature</div>
                    </div>
                    <div class='step active'>
                        <div class='step-number'>3</div>
                        <div class='small mt-2'>Encryption</div>
                    </div>
                    <div class='step'>
                        <div class='step-number'>4</div>
                        <div class='small mt-2'>Finalize</div>
                    </div>
                </div>

                <div class='card shadow-sm form-section'>
                    <div class='card-header bg-primary text-white'>
                        <h5 class='mb-0'>Step 3: Encryption & Permissions</h5>
                    </div>
                    <div class='card-body'>
                        <div class='mb-3'>
                            <label class='form-label'><strong>Encryption Level</strong></label>
                            <select class='form-select'>
                                <option>AES 128-bit</option>
                                <option selected>AES 256-bit (Recommended)</option>
                                <option>RC4 128-bit (Legacy)</option>
                            </select>
                            <small class='text-muted'>AES-256 provides enterprise-grade security</small>
                        </div>

                        <div class='mb-3'>
                            <label class='form-label'><strong>Document Permissions</strong></label>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowPrint' checked>
                                <label class='form-check-label' for='allowPrint'>Allow Printing</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowCopy'>
                                <label class='form-check-label' for='allowCopy'>Allow Content Copying</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowModify'>
                                <label class='form-check-label' for='allowModify'>Allow Document Modification</label>
                            </div>
                            <div class='form-check form-switch'>
                                <input class='form-check-input' type='checkbox' id='allowAnnotate' checked>
                                <label class='form-check-label' for='allowAnnotate'>Allow Annotations</label>
                            </div>
                        </div>

                        <div class='mb-3'>
                            <label class='form-label'><strong>Password Protection</strong></label>
                            <input type='password' class='form-control mb-2' placeholder='Owner Password' value='••••••••'>
                            <input type='password' class='form-control' placeholder='User Password (Optional)'>
                            <small class='text-muted'>Owner password controls document permissions</small>
                        </div>

                        <div class='alert alert-success'>
                            <strong>✓ Configuration Valid</strong><br>
                            <small>Security settings meet compliance requirements</small>
                        </div>
                    </div>
                    <div class='card-footer'>
                        <div class='d-flex justify-content-between'>
                            <button class='btn btn-outline-secondary'>← Previous</button>
                            <button class='btn btn-primary'>Continue →</button>
                        </div>
                    </div>
                </div>

                <div class='card mt-3 shadow-sm'>
                    <div class='card-body'>
                        <h6 class='text-primary'>Security Comparison</h6>
                        <div class='row g-2'>
                            <div class='col-6'>
                                <div class='text-center p-2 bg-success text-white rounded'>
                                    <strong>IronPDF</strong><br>
                                    <small>AES-256 Native</small>
                                </div>
                            </div>
                            <div class='col-6'>
                                <div class='text-center p-2 bg-warning text-dark rounded'>
                                    <strong>iTextSharp</strong><br>
                                    <small>Complex Setup</small>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>"

Dim pdf = renderer.RenderHtmlAsPdf(securityConfigForm)
pdf.SaveAs("security-configuration.pdf")
$vbLabelText   $csharpLabel

Wyjście: Profesjonalny formularz konfiguracji zabezpieczeń PDF z wskaźnikami kroków Bootstrap 5, kontrolkami formularza, przełącznikami i alertami walidacji. IronPDF renderuje całą stylizację formy, klasy użytkowe i interaktywne elementy z perfekcyjną wiernością, demonstrując wyższe możliwości renderowania formularzy w porównaniu do programowego podejścia iTextSharp.

Więcej szczegółów na temat wsparcia dla formularzy Bootstrap znajdziesz w Przewodniku dla CSS Flexbox w Bootstrap.

3. Podpisuj PDFy cyfrowo w C# korzystając z IronPDF

Dołącz niezbędne przestrzenie nazw

Zaczynamy od importu niezbędnych przestrzeni nazw do pracy z podpisami PDF, obsługą certyfikatów i pozycjonowaniem obrazów.

using IronPdf;
using IronPdf.Signing;
using IronSoftware.Drawing;
using System.Security.Cryptography.X509Certificates;
using IronPdf;
using IronPdf.Signing;
using IronSoftware.Drawing;
using System.Security.Cryptography.X509Certificates;
Imports IronPdf
Imports IronPdf.Signing
Imports IronSoftware.Drawing
Imports System.Security.Cryptography.X509Certificates
$vbLabelText   $csharpLabel

Wczytaj PDF, który chcesz podpisać

Ładujemy istniejący plik PDF z dysku przy użyciu prostego API PdfDocument IronPDF. Można również utworzyć nowy dokument PDF do tego zadania.

var pdf = PdfDocument.FromFile("example.pdf");
var pdf = PdfDocument.FromFile("example.pdf");
Dim pdf = PdfDocument.FromFile("example.pdf")
$vbLabelText   $csharpLabel

Wczytaj certyfikat PFX używany do podpisu

Ładujemy certyfikat .pfx zawierający klucz prywatny. Flaga Eksportowalny jest wymagana, aby można było uzyskać dostęp do klucza podpisu.

X509Certificate2 cert = new X509Certificate2(
    "IronSoftware.pfx",
    "Password",
    X509KeyStorageFlags.Exportable
);
X509Certificate2 cert = new X509Certificate2(
    "IronSoftware.pfx",
    "Password",
    X509KeyStorageFlags.Exportable
);
Dim cert As New X509Certificate2("IronSoftware.pfx", "Password", X509KeyStorageFlags.Exportable)
$vbLabelText   $csharpLabel

Stwórz nowy PdfSignature korzystając z certyfikatu

Tworzymy nowy obiekt PdfSignature z załadowanego certyfikatu.

var sig = new PdfSignature(cert);
var sig = new PdfSignature(cert);
Dim sig = New PdfSignature(cert)
$vbLabelText   $csharpLabel

Zastosuj podpis i zapisz wynik

Cyfrowo podpisujemy PDF i zapisujemy podpisany dokument PDF jako nowy plik.

pdf.Sign(sig);
pdf.SaveAs("signed.pdf");
pdf.Sign(sig);
pdf.SaveAs("signed.pdf");
pdf.Sign(sig)
pdf.SaveAs("signed.pdf")
$vbLabelText   $csharpLabel

Wynik

PDF podpisany przy użyciu IronPDF

4. Wyjaśnienie kodu

  • IronPDF utrzymuje proces podpisywania prosty i czytelny. Ładujesz PDF, podajesz certyfikat i wywołujesz SignPdf(). Opcjonalne metadane (kontakt, lokalizacja, powód) dodają profesjonalizmu.

  • iTextSharp daje większą kontrolę, ale wymaga szczegółowej konfiguracji z algorytmami haszowania, strumieniami i łańcuchami certyfikatów.

Podsumowanie: Dzięki zaledwie kilku linijkom kodu, IronPDF sprawia, że stosowanie podpisów cyfrowych przy użyciu standardowych certyfikatów .pfx jest niezwykle proste — brak potrzeby posiadania wiedzy o kryptografii na niskim poziomie. To ułatwia wdrożenie w porównaniu do dłuższego kodu wymaganego przez biblioteki takie jak iTextSharp do obsługi tego samego zadania.

5. Przykłady zastosowań ze świata rzeczywistego

  • Zespoły prawne: Automatyczne podpisywanie umów generowanych z szablonów.

  • Finanse: Cyfrowe podpisywanie faktur i raportów w celu zapobieżenia manipulacji.

  • Portale rządowe: Podpisywanie formularzy przed ich złożeniem w celu spełnienia standardów regulacyjnych.

Najlepsze praktyki dla podpisów cyfrowych

Aby w pełni wykorzystać implementację podpisów cyfrowych:

  • Używaj silnych certyfikatów: Wybieraj klucze RSA 2048-bitowe lub silniejsze.

  • Bezpiecznie przechowuj klucze prywatne: Przechowuj certyfikaty bezpiecznie, najlepiej w module zabezpieczeń sprzętowych (HSM).

  • Znak czasu dla podpisów: Dodaj zaufane znaczki czasu, aby zapewnić, że podpis pozostanie ważny nawet po wygaśnięciu certyfikatu.

  • Weryfikuj podpisy: Włącz weryfikację w swojej aplikacji, aby wykrywać manipulacje lub wygasłe certyfikaty.

  • Automatyzuj: Zaplanuj operacje podpisywania w swojej linii wdrożeniowej, aby zapewnić stałą integralność dokumentów.

Wnioski

Dodawanie podpisów cyfrowych do dokumentów PDF nie jest już luksusem — to konieczność w dzisiejszym cyfrowo świadomym środowisku. Niezależnie od tego, czy chronisz umowy, faktury, raporty, czy dokumenty prawne, posiadanie podpisu wykrywalnego na manipulacje w oparciu o zaufany certyfikat zapewnia, że Twoje pliki zachowują swoją autentyczność i integralność.

W tym artykule zbadaliśmy dwa potężne podejścia do podpisywania PDF w C#:

  • iTextSharp, który daje niskopoziomową kontrolę kryptograficzną i elastyczność, ale wymaga więcej boilerplate i znajomości BouncyCastle.

  • IronPDF, który oferuje nowoczesne, wysokopoziomowe API, które sprawia, że proces aplikowania bezpiecznych podpisów jest płynny i przyjazny dla programisty.

Oba narzędzia obsługują bezpieczne certyfikaty .pfx, ale IronPDF wyraźnie upraszcza proces pracy — jest to idealne rozwiązanie dla programistów .NET, którzy chcą poświęcać mniej czasu na obsługę elementów kryptograficznych, a więcej na dostarczanie wartości biznesowej.

Tabela porównawcza iTextSharp vs IronPDF

Kolejne kroki

Jeśli jeszcze tego nie zrobiłeś, rozważ pobranie bezpłatnej wersji próbnej IronPDF i spróbuj podpisać własne pliki PDF za pomocą zaledwie kilku wierszy kodu. Już sam wzrost wydajności jest wart zmiany, zwłaszcza podczas pracy nad projektami, w których liczy się czas.

Zwróć uwagęiTextSharp jest zarejestrowanym znakiem towarowym odpowiedniego właściciela. Ta strona nie jest powiązana z iTextSharp, nie jest przez nią promowana ani sponsorowana. Wszystkie nazwy produktów, logo i marki są własnością ich odpowiednich właścicieli. Porównania mają charakter wyłącznie informacyjny i odzwierciedlają informacje dostępne publicznie w momencie pisania.

Często Zadawane Pytania

W jaki sposób podpisy cyfrowe zapewniają autentyczność dokumentu PDF?

Podpisy cyfrowe wykorzystują techniki kryptograficzne do weryfikacji tożsamości podpisującego i zapewnienia, że dokument nie został zmieniony. Osiąga się to poprzez zaszyfrowanie skrótu dokumentu za pomocą klucza prywatnego, co zapewnia zarówno uwierzytelnienie, jak i integralność.

Dlaczego podpisy cyfrowe są ważne w przypadku dokumentów elektronicznych?

Podpisy cyfrowe mają kluczowe znaczenie dla zachowania legalności, bezpieczeństwa, wydajności i zaufania do dokumentów elektronicznych. Zapewniają one zgodność z przepisami, zapobiegają manipulacjom, usprawniają przepływ dokumentów oraz potwierdzają pochodzenie i integralność dokumentu.

Jak dodać podpis cyfrowy do pliku PDF w języku C#?

W języku C# można dodać podpis cyfrowy do pliku PDF za pomocą biblioteki IronPDF. Proces ten obejmuje załadowanie pliku PDF, podanie certyfikatu .pfx przy użyciu klasy X509Certificate2 oraz wywołanie metody Sign w celu zastosowania podpisu.

Jakie są główne różnice między iTextSharp a inną biblioteką PDF do podpisów cyfrowych?

iTextSharp oferuje rozbudowaną obsługę szczegółowych operacji na plikach PDF, ale charakteryzuje się stromą krzywą uczenia się i wymaga specjalnej licencji. Natomiast IronPDF jest przyjazny dla początkujących dzięki uproszczonemu interfejsowi API, umożliwia szybkie wdrożenie i nie wymaga licencji open source.

Jak zainstalować bibliotekę PDF do podpisywania dokumentów w języku C#?

Możesz zainstalować IronPDF przez NuGet za pomocą polecenia: Install-Package IronPdf lub za pomocą .NET CLI, używając dotnet add package IronPdf.

Jakie kroki są wymagane do cyfrowego podpisania pliku PDF za pomocą iTextSharp?

Aby podpisać cyfrowo plik PDF za pomocą iTextSharp, należy skonfigurować PdfStamper, dostosować wygląd podpisu, załadować certyfikat .pfx przy użyciu BouncyCastle oraz wykorzystać PdfSigner i IExternalSignature do operacji kryptograficznych.

Jakie są najlepsze praktyki dotyczące wdrażania podpisów cyfrowych w plikach PDF?

Najlepsze praktyki obejmują stosowanie silnych certyfikatów, zabezpieczanie kluczy prywatnych, stosowanie znaczników czasu w podpisach, regularną weryfikację podpisów oraz automatyzację procesu podpisywania w celu zachowania integralności dokumentów.

Jakie są praktyczne zastosowania podpisów cyfrowych w dokumentach PDF?

Podpisy cyfrowe są powszechnie stosowane w działach prawnych do podpisywania umów, w finansach do zatwierdzania faktur i raportów oraz w agencjach rządowych do przetwarzania formularzy w celu zapewnienia zgodności z normami regulacyjnymi.

Która biblioteka PDF jest lepsza dla programistów potrzebujących szybkiego wdrożenia podpisu cyfrowego?

Programistom poszukującym szybkiego wdrożenia podpisów cyfrowych zaleca się IronPDF ze względu na jego proste i przejrzyste API, które upraszcza proces podpisywania, wymagając minimalnej ilości kodu.

Czy IronPDF jest kompatybilny z .NET 10 w zakresie dodawania i weryfikacji podpisów cyfrowych?

Tak — IronPDF jest w pełni kompatybilny z .NET 10. Obsługuje .NET 10 (obok .NET 9, 8, 7 itp.), a jego funkcje podpisu cyfrowego (np. korzystanie z PdfSignature, X509Certificate2, podpisywanie i weryfikacja) działają od razu w środowisku uruchomieniowym .NET 10.

Curtis Chau
Autor tekstów technicznych

Curtis Chau posiada tytuł licencjata z informatyki (Uniwersytet Carleton) i specjalizuje się w front-endowym rozwoju, z ekspertką w Node.js, TypeScript, JavaScript i React. Pasjonuje się tworzeniem intuicyjnych i estetycznie przyjemnych interfejsów użytkownika, Curtis cieszy się pracą z nowoczesnymi frameworkami i tworzeniem dobrze zorganizowanych, atrakcyjnych wizualnie podrę...

Czytaj więcej

Zespol wsparcia Iron

Jestesmy online 24 godziny, 5 dni w tygodniu.
Czat
Email
Zadzwon do mnie