简体   繁体   English

使用 c# 使用 sha256 进行数字签名

[英]Digital sign with sha256 with c#

here in Italy, we will need to digitally sign all invoices since January 2019.在意大利,自 2019 年 1 月起,我们将需要对所有发票进行数字签名。

I found a code that works well with sha-1, but I need to use sha256 as standard.我找到了一个适用于 sha-1 的代码,但我需要使用 sha256 作为标准。 The code below, after successfully detect USB key, and ask me for the certificate to use try to sign "NomeFile" file After and output in "NomeFile".p7m, when the line下面的代码,在成功检测到USB密钥后,并问我要使用的证书尝试签署“NomeFile”文件后并在“NomeFile”.p7m中输出,当行

signedCms.ComputeSignature(signer,false); signedCms.ComputeSignature(signer,false);

runs, it happens: 1- if use sha-1 it asks me for the PIN and document is successfully created.运行,它会发生: 1- 如果使用 sha-1,它会要求我输入 PIN 并成功创建文档。 2- if use sha-256 don't ask for PIN and gives me Unknown error -1073741275 2- 如果使用 sha-256 不要求输入 PIN 并给我未知错误 -1073741275

I read a lot of posts that are all old (2011-2014).我阅读了很多旧帖子(2011-2014)。 Other people have the same problem and seem that Microsoft has some bug using sha256.其他人也有同样的问题,似乎微软在使用 sha256 时有一些错误。

Now we are at end of 2018 and I tried this code in .net 4, 4.6.1 and 4.7.2 but the error is the same.现在我们是 2018 年底,我在 .net 4、4.6.1 和 4.7.2 中尝试了这段代码,但错误是一样的。

Somebody can tell me if Microsoft corrects the problem with sha256 and what could be this strange error?有人可以告诉我微软是否用 sha256 解决了这个问题,这个奇怪的错误是什么? (-1073741275) Error Stack (-1073741275)错误堆栈

public String FirmaFile(String NomeFile, DateTime DataFirma, X509Certificate2 cert, out string RisFirma)
        {
            String NomeFirma = NomeFile + ".p7m";
            RisFirma = "";

            try
            {


                // content contiene il file da firmare
                ContentInfo content = new ContentInfo((File.ReadAllBytes(NomeFile)));
                // assegniamo content ad un oggetto di tipo SignedCms
                SignedCms signedCms = new SignedCms(SubjectIdentifierType.IssuerAndSerialNumber, content, false);

                // si instanzia un oggetto CmsSigner che espone i metodi di firma.
                CmsSigner signer = new CmsSigner(cert);
                signer.IncludeOption = X509IncludeOption.EndCertOnly;

                //signer.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");
                signer.DigestAlgorithm = new Oid("SHA256");
                signer.SignedAttributes.Add(new Pkcs9SigningTime(DataFirma));
                try
                {
                    // Viene calcolata la firma del file (in formato PKCS7)
                    signedCms.ComputeSignature(signer,false);
                }
                catch (CryptographicException CEx)
                {
                    RisFirma = "Errore: " + CEx.Message + " Stack: " + CEx.StackTrace;
                    return RisFirma;
                }
                // si pone il file firmato in un array.
                byte[] signature = signedCms.Encode();
                File.WriteAllBytes(NomeFirma, signature);
                RisFirma = "true";
            }
            catch (Exception Ex)
            {
                RisFirma = "Errore in FirmaFile: " + Ex.Message + " Stack: " + Ex.StackTrace;
            }
            return RisFirma;
        }

NB: I tried 2 version of OID signer.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1");注意:我尝试了 2 个版本的 OID signer.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1"); signer.DigestAlgorithm = new Oid("SHA256"); signer.DigestAlgorithm = new Oid("SHA256");

All 2 give the same error.所有 2 都给出相同的错误。

I USE an INFOCERT USB KEY with driver bit4id ( https://www.bit4id.com/it/4identity/ ) that is contained in USB Drive.我使用包含在 USB 驱动器中的驱动程序 bit4id ( https://www.bit4id.com/it/4identity/ ) 的 INFOCERT USB KEY。

The error and symptom seem to indicate that the CSP (Cryptographic Service Provider) which is doing the signing operation doesn't support SHA-2.错误和症状似乎表明执行签名操作的 CSP(加密服务提供商)不支持 SHA-2。 If it's working in BouncyCastle then they are seemingly exporting the private key and re-importing it into their software provider.如果它在 BouncyCastle 中工作,那么他们似乎在导出私钥并将其重新导入到他们的软件提供商中。

In .NET 4.7.2 you could try the following:在 .NET 4.7.2 中,您可以尝试以下操作:

...
try
{
    // Viene calcolata la firma del file (in formato PKCS7)
    signedCms.ComputeSignature(signer,false);
}
catch (CryptographicException CEx)
{
    try
    {
        // Try re-importing the private key into a better CSP:
        using (RSA tmpRsa = RSA.Create())
        {
            tmpRsa.ImportParameters(cert.GetRSAPrivateKey().ExportParameters(true));

            using (X509Certificate2 tmpCertNoKey = new X509Certificate2(cert.RawData))
            using (X509Certificate2 tmpCert = tmpCertNoKey.CopyWithPrivateKey(tmpRsa))
            {
                signer.Certificate = tmpCert;
                signedCms.ComputeSignature(signer,false);
            }
        }
    }
    catch (CryptographicException)
    {
        // This is the original exception, not the inner one.
        RisFirma = "Errore: " + CEx.Message + " Stack: " + CEx.StackTrace;
        return RisFirma;
    }
}

If the certificate is actually being loaded from a PFX file on the USB device, then the problem is that the PFX specifies to use an older software CSP which predated SHA-2.如果证书实际上是从 USB 设备上的 PFX 文件加载的,则问题在于 PFX 指定使用早于 SHA-2 的旧软件 CSP。 Getting the PFX regenerated to use the newest RSA CSP would also solve the problem.重新生成 PFX 以使用最新的 RSA CSP 也可以解决问题。

use this:用这个:

private string podpisz(X509Certificate2 cert, string toSign)
{
    string output = "";

    try
    {
        RSACryptoServiceProvider csp = null;
        csp = (RSACryptoServiceProvider)cert.PrivateKey;

        // Hash the data
        SHA256Managed sha256 = new SHA256Managed();
        UnicodeEncoding encoding = new UnicodeEncoding();
        byte[] data = Encoding.Default.GetBytes(toSign);
        byte[] hash = sha256.ComputeHash(data);

        // Sign the hash
        byte[] wynBin = csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA256"));
        output = Convert.ToBase64String(wynBin);

    }
    catch (Exception)
    {

    }

    return output;
}

I found this in internet, I try it and incredibly it worked!我在互联网上找到了这个,我尝试了一下,结果令人难以置信! Anyway the solution is bit more complex.无论如何,解决方案有点复杂。

You have to use BouncyCastle ( https://www.bouncycastle.org/ ) library.您必须使用 BouncyCastle ( https://www.bouncycastle.org/ ) 库。 But not the version avaiable, but a version that was modified by on user on another forum.但不是可用的版本,而是由另一个论坛上的用户修改的版本。

The link to the bouncy castle library modified is: http://www.mediafire.com/download/uc63d1hepqyuhee/bccrypto-net-1.7-src-ext_with_CADES-BES.zip修改后的充气城堡库的链接是: http : //www.mediafire.com/download/uc63d1hepqyuhee/bccrypto-net-1.7-src-ext_with_CADES-BES.zip

You have to use crypto.dll library found in bin\\release and reference it in your project.您必须使用在 bin\\release 中找到的 crypto.dll 库并在您的项目中引用它。

Theese are all using I have now, probably not all are required for this specified case:这些都在使用我现在所拥有的,对于这个特定的情况,可能不是所有的都需要:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.IO;
using System.Collections;
using CryptoUpgNet.NonExportablePK;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Ess;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;

This is the function:这是函数:

public byte[] FirmaFileBouncy(String NomeFile, X509Certificate2 cert, ref string RisFirma)
        {
            String NomeFirma = NomeFile + ".p7m";

            try
            {
                SHA256Managed hashSha256 = new SHA256Managed();
                byte[] certHash = hashSha256.ComputeHash(cert.RawData);

                EssCertIDv2 essCert1 = new EssCertIDv2(new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier("2.16.840.1.101.3.4.2.1"), certHash);
                SigningCertificateV2 scv2 = new SigningCertificateV2(new EssCertIDv2[] { essCert1 });

                Org.BouncyCastle.Asn1.Cms.Attribute CertHAttribute = new Org.BouncyCastle.Asn1.Cms.Attribute(Org.BouncyCastle.Asn1.Pkcs.PkcsObjectIdentifiers.IdAASigningCertificateV2, new DerSet(scv2));
                Asn1EncodableVector v = new Asn1EncodableVector();
                v.Add(CertHAttribute);

                Org.BouncyCastle.Asn1.Cms.AttributeTable AT = new Org.BouncyCastle.Asn1.Cms.AttributeTable(v);
                CmsSignedDataGenWithRsaCsp cms = new CmsSignedDataGenWithRsaCsp();

                dynamic rsa = (RSACryptoServiceProvider)cert.PrivateKey;
                Org.BouncyCastle.X509.X509Certificate certCopy = DotNetUtilities.FromX509Certificate(cert);
                cms.MyAddSigner( rsa, certCopy,  "1.2.840.113549.1.1.1", "2.16.840.1.101.3.4.2.1", AT, null);

                ArrayList certList = new ArrayList();
                certList.Add(certCopy);

                Org.BouncyCastle.X509.Store.X509CollectionStoreParameters PP = new Org.BouncyCastle.X509.Store.X509CollectionStoreParameters(certList);
                Org.BouncyCastle.X509.Store.IX509Store st1 = Org.BouncyCastle.X509.Store.X509StoreFactory.Create("CERTIFICATE/COLLECTION", PP);

                cms.AddCertificates(st1);

                //mi ricavo il file da firmare
                FileInfo File__1 = new FileInfo(NomeFile);
                CmsProcessableFile file__2 = new CmsProcessableFile(File__1);
                CmsSignedData Firmato = cms.Generate(file__2, true);
                byte[] Encoded = Firmato.GetEncoded();

                File.WriteAllBytes(NomeFirma, Encoded);

                RisFirma = "true";

                return Encoded;

            } catch (Exception ex)  {

                RisFirma = ex.ToString();
                return null;
            }

        }

Edit: Using it repeatly with same certificate, it ask PIN only the first time.编辑:使用相同的证书重复使用它,它只在第一次询问 PIN 码。 So is good to make multiple files at once with security standard active.因此,在安全标准处于活动状态的情况下同时制作多个文件是很好的。

To Grzegorz:致格泽戈尔茨:

The source file is 5k The correct signed file (IT01234567890_FPA01_2.xml.p7m is 8k The file saved with your routine adding a源文件为 5k 正确签名文件(IT01234567890_FPA01_2.xml.p7m 为 8k 与您的例程保存的文件添加

File.WriteAllBytes("c:\\temp\\IT01234567890_FPA01.xml.p7m", wynBin);

after

byte[] wynBin = csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA256"));

is only 1kb and is not reckognized by Dike.只有 1kb 并且不被 Dike 识别。 Sign not reckognized无法识别的标志

Difference between files文件之间的差异

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM