Validasi tanda tangan elektronik di C # menggunakan Crypto PRO

Melanjutkan pembicaraan tentang topik tanda tangan elektronik (selanjutnya disebut tanda tangan elektronik), harus dikatakan tentang verifikasi. Pada artikel sebelumnya, saya menganalisis bagian yang lebih sulit dari tugas - membuat tanda tangan. Artikel ini sedikit lebih sederhana. Sebagian besar kode adalah adaptasi contoh dari Crypto PRO .NET SDK. Kami akan memeriksa terlebih dahulu semua tanda tangan sesuai dengan GOST R 34.10-2001 dan GOST R 34.10-2012, untuk ini kita perlu CRYPTO PRO.

Tugas kami dibagi menjadi 3 bagian: tanda tangan terpisah, tanda tangan dalam PDF dan tanda tangan dalam MS Word.

Verifikasi Tanda Tangan Terpisah:

//dataFileRawBytes -     ContentInfo contentInfo = new ContentInfo(dataFileRawBytes); SignedCms signedCms = new SignedCms(contentInfo, true); //signatureFileRawBytes -    signedCms.Decode(signatureFileRawBytes); if (signedCms.SignerInfos.Count == 0) { //     } foreach (SignerInfo signerInfo in signedCms.SignerInfos) { //   DateTime? signDate = (signerInfo.SignedAttributes .Cast<CryptographicAttributeObject>() .FirstOrDefault(x => x.Oid.Value == "1.2.840.113549.1.9.5") ?.Values[0] as Pkcs9SigningTime)?.SigningTime; bool valid; try { signerInfo.CheckSignature(true); valid = true; } catch (CryptographicException exc) { valid = false; } //   .     X509Certificate2 certificate = signerInfo.Certificate; 

Semua komentar ada dalam kode, saya hanya akan menarik perhatian Anda untuk mendapatkan sertifikat, kami akan membutuhkannya nanti, karena kami akan memeriksa sertifikat secara terpisah.

Nah, jangan lupa untuk membungkus semuanya dalam try-catch dan yang lainnya gunakan. Dalam contoh, saya sengaja tidak melakukan ini untuk mengurangi volume

Validasi tanda tangan dalam PDF. Di sini kita membutuhkan iTextSharp (versi saat ini pada saat penulisan 5.5.13):

  using (MemoryStream fileStream = new MemoryStream(dataFileRawBytes)) using (PdfReader pdfReader = new PdfReader(fileStream)) { AcroFields acroFields = pdfReader.AcroFields; //    List<string> signatureNames = acroFields.GetSignatureNames(); if (!signatureNames.Any()) { //   } foreach (string signatureName in signatureNames) { //       PdfDictionary singleSignature = acroFields.GetSignatureDictionary(signatureName); PdfString asString1 = singleSignature.GetAsString(PdfName.CONTENTS); byte[] signatureBytes = asString1.GetOriginalBytes(); RandomAccessFileOrArray safeFile = pdfReader.SafeFile; PdfArray asArray = singleSignature.GetAsArray(PdfName.BYTERANGE); using ( Stream stream = new RASInputStream( new RandomAccessSourceFactory().CreateRanged( safeFile.CreateSourceView(), asArray.AsLongArray()))) { using (MemoryStream ms = new MemoryStream((int)stream.Length)) { stream.CopyTo(ms); byte[] data = ms.GetBuffer(); ContentInfo contentInfo = new ContentInfo(data); SignedCms signedCms = new SignedCms(contentInfo, true); signedCms.Decode(signatureBytes); bool checkResult; //    ,    try { signedCms.CheckSignature(true); checkResult = true; } catch (Exception) { checkResult = false; } foreach (SignerInfo signerInfo in signedCms.SignerInfos) { //   DateTime? signDate = (signerInfo.SignedAttributes .Cast<CryptographicAttributeObject>() .FirstOrDefault(x => x.Oid.Value == "1.2.840.113549.1.9.5") ?.Values[0] as Pkcs9SigningTime)?.SigningTime; //  X509Certificate2 certificate = signerInfo.Certificate; } } } } } 

Sekali lagi, tidak ada yang perlu dikomentari. Kecuali saya harus mengatakan tentang Oid, "1.2.840.113549.1.9.5" adalah Oid dari tanggal penandatanganan.

Dan yang terakhir dalam daftar kami adalah docx, mungkin opsi termudah:

  using (MemoryStream fileStream = new MemoryStream(dataFileRawBytes)) using (Package filePackage = Package.Open(fileStream)) { PackageDigitalSignatureManager digitalSignatureManager = new PackageDigitalSignatureManager(filePackage); if (!digitalSignatureManager.IsSigned) { //    } foreach (PackageDigitalSignature signature in digitalSignatureManager.Signatures) { DateTime? signDate = signature.SigningTime; bool checkResult = signature.Verify() == VerifyResult.Success; //      X509Certificate2 certificate = new X509Certificate2(signature.Signer); } } 

Sekarang kita akan menguraikan sertifikat dan memvalidasi seluruh rantai sertifikat. Oleh karena itu, perakitan harus bekerja dari bawah pengguna yang memiliki akses ke jaringan.

Dan kemudian neraka dimulai, karena Saya tidak tahu cara mendapatkan informasi tentang pemilik sertifikat melalui Oid, jadi saya akan menguraikan string. Tertawa lebih keras: sirkus dimulai.

Tapi serius, Anda dipersilakan untuk mengomentari mereka yang tahu bagaimana melakukan ini melalui Oid:

  private static void FillElectronicSignature(X509Certificate2 certificate) { foreach (KeyValuePair<string, string> item in ParseCertificatesSubject(certificate.Subject)) { switch (item.Key) { case "C": string certificatesCountryName = item.Value; break; case "S": string certificatesState = item.Value; break; case "L": string certificatesLocality = item.Value; break; case "O": string certificatesOrganizationName = item.Value; break; case "OU": string certificatesOrganizationalUnitName = item.Value; break; case "CN": string certificatesCommonName = item.Value; break; case "E": string certificatesEmail = item.Value; break; case "STREET": string certificatesStreet = item.Value; break; //  ,  Window ,     ,   ,  INN //       deploy    // ,   -  case "": case "INN": case "1.2.643.3.131.1.1": string certificatesInn = item.Value; break; //  case "": case "OGRN": case "1.2.643.100.1": string certificatesOgrn = item.Value; break; //  case "": case "SNILS": case "1.2.643.100.3": string certificatesSnils = item.Value; break; case "SN": string certificatesOwnerLastName = item.Value; break; case "G": string certificatesOwnerFirstName = item.Value; break; //    default           } } DateTime certificateNotBefore = certificate.NotBefore; DateTime certificateNotAfter = certificate.NotAfter; string certificatesSerialNumber = certificate.SerialNumber; if (!certificate.Verify()) { //   using (X509Chain x509Chain = new X509Chain()) { x509Chain.Build(certificate); //    X509ChainStatus[] statuses = x509Chain.ChainStatus; //      int,    int certificatesErrorCode = statuses.Aggregate(X509ChainStatusFlags.NoError, (acc, chainStatus) => acc | chainStatus.Status, result => (int)result); } } } /// <summary> ///        /// </summary> private static Dictionary<string, string> ParseCertificatesSubject(string subject) { Dictionary<string, string> result = new Dictionary<string, string>(); //  ,     int quotationMarksCount = 0; //    "  " bool isKey = true; //    string key = string.Empty; //    string value = string.Empty; for (int i = 0; i < subject.Length; i++) { char c = subject[i]; if (isKey && c == '=') { isKey = false; continue; } if (isKey) key += c; else { if (c == '"') quotationMarksCount++; bool isItemEnd = (c == ',' && subject.Length >= i + 1 && subject[i + 1] == ' '); bool isLastChar = subject.Length == i + 1; if ((isItemEnd && quotationMarksCount % 2 == 0) || isLastChar) { if (isItemEnd) i++; if (isLastChar) value += c; isKey = true; if (value.StartsWith("\"") && value.EndsWith("\"")) value = value.Substring(1, value.Length - 2); value = value.Replace("\"\"", "\""); result.Add(key, value); key = string.Empty; value = string.Empty; quotationMarksCount = 0; continue; } value += c; } } return result; } 

Kode ini sesingkat mungkin, untuk lebih memahami esensi.

Secara umum, ini saja, saya menunggu komentar tentang mendapatkan Oid dari sertifikat dan kritik apa pun yang beralasan.

Source: https://habr.com/ru/post/id426645/


All Articles