In questo esempio, scritto in C# per applicazioni Windows Forms, vedremo come estrarre alcune informazioni dalle firme digitali presenti in un file PDF, senza utilizzare librerie di terze parti.
Dopo aver selezionato il file PDF, il codice verifica la presenza di una o più firme digitali. Se ne trova almeno una, legge il contenuto del certificato associato a ciascuna firma ed estrae alcune informazioni, tra cui:
il soggetto del certificato;
l'autorità che lo ha emesso;
il periodo di validità;
l'algoritmo di firma;
il numero di serie del certificato.
L'esempio mostra una delle possibili tecniche per leggere le informazioni dei certificati associati alle firme digitali presenti in un file PDF. Poiché la struttura interna di un PDF può variare (ad esempio in presenza di oggetti compressi o differenti modalità di memorizzazione della firma), questo approccio potrebbe non funzionare con tutti i documenti. L'obiettivo dell'articolo è illustrare il funzionamento della tecnica, senza realizzare un parser completo del formato PDF.
Di seguito è riportata la funzione che converte una stringa esadecimale in un array di byte.
C#
byte[] HexStringToBytes(string hex)
{
if (hex.Length % 2 !=
0)
throw new ArgumentException("Stringa esadecimale non valida.");
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
return bytes;
}
Di seguito è riportato il codice dell'evento Click del pulsante, che individua le firme digitali presenti nel PDF, legge il certificato associato a ciascuna di esse ed estrae le principali informazioni.
C#
private void BtnLeggiCertificatiPdf_Click(object sender, EventArgs e)
{
try
{
dtgCertificatiFilePdf.DataSource = null;
//Verifico se è stato selezionato il file
if (string.IsNullOrEmpty(TxtPercorsoCartella.Text.Trim()))
{
Utility.MessaggioInfo("Selezionare un file PDF.");
return;
}
List<DtoFirmaDigitale>
certificatiTrovati =
new List<DtoFirmaDigitale>();
string pdfPath =
@TxtPercorsoCartella.Text.Trim();
byte[] pdfBytes =
File.ReadAllBytes(pdfPath);
string pdfText = Encoding.ASCII.GetString(pdfBytes);
// Trova TUTTI i blocchi /Contents <...>
MatchCollection matches = Regex.Matches(pdfText, @"/Contents\s*<([0-9A-Fa-f]+)>");
if (matches.Count == 0)
{
Utility.MessaggioInfo("Nessuna firma digitale trovata nel PDF.");
return;
}
int firmaIndex =
1;
foreach (Match match in matches)
{
try
{
byte[]
signatureBytes =
HexStringToBytes(match.Groups[1].Value);
SignedCms cms =
new SignedCms();
cms.Decode(signatureBytes);
foreach (var firma in cms.SignerInfos)
{
X509Certificate2 cert =
firma.Certificate;
DtoFirmaDigitale dtoCertificato = new DtoFirmaDigitale()
{
Oggetto = cert.Subject,
EmessoDa = cert.Issuer,
DataInizio = cert.NotBefore,
DataFine = cert.NotAfter,
Algoritmo = cert.SignatureAlgorithm.FriendlyName,
Seriale = cert.SerialNumber
};
certificatiTrovati.Add(dtoCertificato);
}
}
catch (Exception ex)
{
Utility.MessaggioErrore($"Errore nella lettura della firma #{firmaIndex}: {ex.Message}\n");
}
firmaIndex++;
}
dtgCertificatiFilePdf.DataSource = certificatiTrovati;
}
catch (Exception ex)
{
Utility.MessaggioErrore(ex.Message);
}
}