Validación de firmas electrónicas en C # usando Crypto PRO

Continuando con la conversación sobre el tema de las firmas electrónicas (en adelante, firma electrónica), se debe decir sobre la verificación. En el artículo anterior, analicé la parte más difícil de la tarea: crear una firma. Este artículo es un poco más simple. La mayor parte del código es una adaptación de ejemplos de Crypto PRO .NET SDK. Primero verificaremos las firmas de acuerdo con GOST R 34.10-2001 y GOST R 34.10-2012, para esto necesitamos CRYPTO PRO.

La tarea para nosotros se divide en 3 partes: una firma separada, una firma en PDF y una firma en MS Word.

Verificación de firma separada:

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

Todos los comentarios están en el código, solo llamaré su atención para obtener un certificado, lo necesitaremos más tarde, porque Comprobaremos el certificado por separado.

Bueno, no olvides envolver todo en try-catch y otros usando. En el ejemplo, intencionalmente no hago esto para reducir el volumen

Validación de firma en PDF. Aquí necesitamos iTextSharp (la versión actual al momento de escribir 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; } } } } } 

Nuevamente, especialmente no hay nada para comentar. A menos que tenga que decir sobre Oid, "1.2.840.113549.1.9.5" es el Oid de la fecha de firma.

Y el último en nuestra lista es docx, quizás la opción más fácil:

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

Ahora analizaremos el certificado y validaremos toda la cadena de certificados. Por lo tanto, el ensamblaje debería funcionar desde debajo de un usuario que tiene acceso a la red.

Y entonces comienza el infierno, porque No sé cómo obtener información sobre el propietario del certificado a través de Oid, por lo que analizaré la cadena. Ríe más fuerte: comienza el circo.

Pero en serio, puedes comentar sobre aquellos que saben cómo hacer esto a través de 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; } 

El código es lo más corto posible, para una mejor comprensión de la esencia.

En general, esto es todo, estoy esperando comentarios sobre la obtención de Oid del certificado y cualquier crítica razonada.

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


All Articles