Validation des signatures électroniques en C # à l'aide de Crypto PRO

Poursuivant la conversation sur le sujet des signatures électroniques (ci-après dénommées signature électronique), il convient de parler de vérification. Dans l'article précédent, j'ai analysé la partie la plus difficile de la tâche - créer une signature. Cet article est un peu plus simple. La plupart du code est une adaptation d'exemples du SDK Crypto PRO .NET. Nous vérifierons tout d'abord les signatures selon GOST R 34.10-2001 et GOST R 34.10-2012, pour cela nous avons besoin de CRYPTO PRO.

La tâche pour nous est divisée en 3 parties: une signature distincte, une signature en PDF et une signature en MS Word.

Vérification de la signature séparée:

//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; 

Les commentaires sont tous dans le code, je vais seulement attirer votre attention sur l'obtention d'un certificat, nous en aurons besoin plus tard, car nous vérifierons le certificat séparément.

Eh bien, n'oubliez pas de tout emballer dans try-catch et autres en utilisant. Dans l'exemple, je ne le fais pas intentionnellement afin de réduire le volume

Validation de signature en PDF. Ici, nous avons besoin d'iTextSharp (la version actuelle au moment de la rédaction du 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; } } } } } 

Encore une fois, il n'y a surtout rien à commenter. Sauf si je dois dire à propos d'Oid, "1.2.840.113549.1.9.5" est l'Oid de la date de signature.

Et le dernier sur notre liste est docx, peut-être l'option la plus simple:

  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); } } 

Nous allons maintenant analyser le certificat et valider toute la chaîne de certificats. Par conséquent, l'assembly doit fonctionner sous un utilisateur qui a accès au réseau.

Et puis l'enfer commence, car Je ne sais pas comment obtenir des informations sur le propriétaire du certificat via Oid, je vais donc analyser la chaîne. Riez plus fort: le cirque commence.

Mais sérieusement, vous êtes invités à commenter ceux qui savent le faire via 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; } 

Le code est le plus court possible, pour une meilleure compréhension de l'essence.

En général, c'est tout, j'attends des commentaires sur l'obtention d'Oid à partir du certificat et toute critique raisonnée.

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


All Articles