locked
The operation was canceled by the user in an application that uses digital signature under IIS RRS feed

  • Question

  • User-35855200 posted

    The idea of the web app is to sign documents with a digital signature that is loaded from a smart card.

    It is published and set to work on a local user machine. I am using IIS for that matter to set the bindings and enable to accept client certificates.

    It communicates with a web app that is hosted on the cloud.

    I am successfully getting the certificate from the smart card and the private key as well.

    I use the private key to sign the document.

     private InvoiceResult SignDocument(XmlDocument doc)
        {
            InvoiceResult resultValue;
    
            try
            {
                var (resultValue2, certificate) = GetDefaultCertificateStoredOnTheCard();
                resultValue = resultValue2;
                SignXmlDocumentWithCertificate(doc, certificate);
    
                resultValue = InvoiceResult.Success;
            }
            catch (Exception ex)
            {
                _log.TraceInformation($"Error when compute signature and it is : {ex.Message}");
                _log.TraceInformation($"Additional info => stack trace : {ex.StackTrace}");
    
                resultValue = InvoiceResult.CannotSignXmlFiles;
            }
            return resultValue;
        }
    
    
     public (InvoiceResult resultValue, X509Certificate2 cert) GetDefaultCertificateStoredOnTheCard()
        {
    
            var resultValue = InvoiceResult.Success;
    
            using X509Store x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    
            X509Store store = x509Store;
            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
    
            X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindByTimeValid, DateTime.Now, true);
    
            certs = certs.Find(X509FindType.FindByThumbprint, Settings.Default.Thumbprint, true);
    
    
            if (certs.Count == 0)
            {
                resultValue = InvoiceResult.CannotFindSignature;
            }
            X509Certificate2 cert = certs[0];
            if (cert.HasPrivateKey)
            {
                // software cert
                _ = cert.PrivateKey as RSACryptoServiceProvider;
    
            }
            else
            {
                // certificate from smartcard
                CspParameters csp = new CspParameters(1, "Microsoft Base Smart Card Crypto Provider")
                {
                    Flags = CspProviderFlags.UseDefaultKeyContainer
                };
                _ = new RSACryptoServiceProvider(csp);
            }
    
    
            return (resultValue, cert);
        }
    
        private InvoiceResult SignXmlDocumentWithCertificate(XmlDocument xmlDoc, X509Certificate2 cert)
        {
            InvoiceResult resultValue = InvoiceResult.Success;
    
            SignedXml signedXml = new SignedXml(xmlDoc)
            {
                //we will sign it with private key
                SigningKey = cert.PrivateKey
            };
    
            if (cert.PrivateKey == null)
            {
                resultValue = InvoiceResult.CannotSignXmlFiles;
    
                //   throw new ArgumentException("Please make sure the application for electronic signatures is installed, so the private key can be obtained from the smart card!");
            }
    
    
            Reference reference = new Reference
            {
                //sign the entire doc
                Uri = ""
            };
            XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
            reference.AddTransform(env);
            signedXml.AddReference(reference);
    
            //PublicKey part
            RSACryptoServiceProvider rsaprovider = (RSACryptoServiceProvider)cert.PublicKey.Key;
            RSAKeyValue rkv = new RSAKeyValue(rsaprovider);
    
            KeyInfo keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(cert));
            //We add the public key here
            keyInfo.AddClause(rkv);
    
            signedXml.KeyInfo = keyInfo;
            _log.TraceInformation($"Cert has private key or not? {cert.HasPrivateKey}");
    
            signedXml.ComputeSignature();
    
            // Get the XML representation of the signature and save
            // it to an XmlElement object.
            _log.TraceInformation($"It computes the signature succesfully");
    
            XmlElement xmlDigitalSignature = signedXml.GetXml();
    
            // Append the element to the XML document.
            xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
    
            _log.TraceInformation($"It appends the signature succesfully");
    
    
            return resultValue;
        }

    It works fine on Release/Debug but not in Publish. It gets a popup, asks for a PIN and once the PIN has been entered the docs are signed.

    It gets to the signedxml.ComputeSignature and it returns an error :

    The operation was canceled by the user.

    Here is the exception that has been thrown :

    System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr) at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature) at System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash) at System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash) at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()

    The only way I get the same error on release/debug is if I cancel the window which asks for a PIN.

    It  is an IIS setting and a permission issue, but I have tried various things to no avail. The certificate can be found if I require SSL on Client Side is ticked and set it to Accept as in the image :

    I have also tried exporting the private key which I saw on various posts, however, because it is a smart card I am unable to export the Private Key, I can only use it which is what I am doing with my code.

    I also tried to run the same app from the .exe which succesfully digitally signs the xml but the window won't even open and asks for the user pin. I even tried to call Powershell and execute the script in that folder but once it is published - it does not execute nor opens a window.

    Tuesday, December 10, 2019 8:57 AM

All replies

  • User-719153870 posted

    Hi DimitarGrozdanov,

    It works fine on Release/Debug but not in Publish. It gets a popup, asks for a PIN and once the PIN has been entered the docs are signed.

    It gets to the signedxml.ComputeSignature and it returns an error :

    The operation was canceled by the user.

    According to the description, you are using an Interactive Service in IIS which "display a user interface and receive user input." , in your case, a popup to receive a PIN input.

    As you can see at the start of the document,

    "Services cannot directly interact with a user as of Windows Vista. Therefore, the techniques mentioned in the section titled Using an Interactive Service should not be used in new code.".

    This kind of technology is outdate and IIS does not support user interaction anymore. That's why your program can work in local but throw error in IIS.

    Best Regard,

    Yang Shen

    Wednesday, December 11, 2019 10:01 AM