0

here in Italy, we will need to digitally sign all invoices since January 2019.

I found a code that works well with sha-1, but I need to use sha256 as standard. 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

signedCms.ComputeSignature(signer,false);

runs, it happens: 1- if use sha-1 it asks me for the PIN and document is successfully created. 2- if use sha-256 don't ask for PIN and gives me Unknown error -1073741275

I read a lot of posts that are all old (2011-2014). Other people have the same problem and seem that Microsoft has some bug using 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.

Somebody can tell me if Microsoft corrects the problem with sha256 and what could be this strange error? (-1073741275) Error Stack

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"); signer.DigestAlgorithm = new Oid("SHA256");

All 2 give the same error.

I USE an INFOCERT USB KEY with driver bit4id (https://www.bit4id.com/it/4identity/) that is contained in USB Drive.

Mago Maverick
  • 33
  • 1
  • 9

4 Answers4

2

The error and symptom seem to indicate that the CSP (Cryptographic Service Provider) which is doing the signing operation doesn't support SHA-2. If it's working in BouncyCastle then they are seemingly exporting the private key and re-importing it into their software provider.

In .NET 4.7.2 you could try the following:

...
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. Getting the PFX regenerated to use the newest RSA CSP would also solve the problem.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • Thank you @bartonjs, I will try it in next week. – Mago Maverick Oct 21 '18 at 08:39
  • I call this code from .NET Core API service and your answer fixed the exception that was thrown (Invalid type specified), but calling it from .NET framework API service throws (Unknown error "-1073741275"). the library itself is .NET Framework 4.7.2. but fortunately, I use the .NET core service, so thank you :) – mshwf Jan 25 '21 at 07:58
0

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;
}
Grzegorz J
  • 403
  • 3
  • 6
0

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. 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

You have to use crypto.dll library found in bin\release and reference it in your project.

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. So is good to make multiple files at once with security standard active.

Mago Maverick
  • 33
  • 1
  • 9
0

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

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. Sign not reckognized

Difference between files

Mago Maverick
  • 33
  • 1
  • 9