2

I`m trying to load certificate from path and getting internal server error on windows server. While I do it on windows 10 everything works fine.

Not working console application Code

var path = args[0];
var password = args[1];
var certificate2 = new X509Certificate2(path, password);

But getting error

Unhandled exception. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred.
   at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore(Byte[] rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
   at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
   at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)
   at CertCoreTest.Program.Main(String[] args) in C:\Users\Admin\Documents\Visual Studio 2019\Projects\CertTest\CertCoreTest\Program.cs:line 12

Hack working code (not sure why it works)

var path = args[0];
var password = args[1];

Chilkat.Cert cert = new Chilkat.Cert();
var success = cert.LoadPfxData(File.ReadAllBytes(path), password);
if (success == false)
{
    throw new Exception(cert.LastErrorText);
}

var bytes = cert.ExportToPfxData(password, true);
var ceeert = new X509Certificate2(bytes, password);

How to make it work on windows server without using chilkat library?

Qba
  • 148
  • 1
  • 7
  • 25
  • Are you running the exact same executable on both platforms? And the same certificate file? Are you running as administrator on both? – Neil Oct 20 '21 at 16:17
  • Yes I`m running the exact same – Qba Oct 20 '21 at 16:22
  • 1
    I wonder if the algorithm you use in the PFX is not available 9 years ago? – Neil Oct 20 '21 at 16:24
  • Some very good suggestions [here](https://github.com/dotnet/runtime/issues/23437). This is probably related to an underlying permission issue. The certificate may store some information to access a store the user is not granted access on the server (i.e. Machine). Or opening a store may help as suggested in the thread may help. – Hazrelle Oct 20 '21 at 16:45
  • Tried all suggests from your link. Didnt help. – Qba Oct 20 '21 at 16:57
  • How did you create the PFX? What algorithm was used to generate it? – Neil Oct 22 '21 at 09:26
  • Not sure but I can send you generated p12 file to check it out. – Qba Oct 22 '21 at 17:04
  • Have you tried different X509KeyStorageFlags? https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2.-ctor?view=net-5.0#System_Security_Cryptography_X509Certificates_X509Certificate2__ctor_System_String_System_Security_SecureString_System_Security_Cryptography_X509Certificates_X509KeyStorageFlags_ – tiberriver256 Oct 24 '21 at 22:42
  • Yes I tried different key storage flags – Qba Oct 25 '21 at 08:10
  • Some things that can cause this [are mentioned here](https://github.com/dotnet/runtime/issues/23437#issuecomment-328564395). You can try the following flags: `X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.MachineKeySet`. Did you try it with elevanted priviledges too? – kapsiR Oct 25 '21 at 08:37
  • And some more information [here too](https://github.com/dotnet/runtime/issues/19774#issuecomment-295366419). – kapsiR Oct 25 '21 at 08:43
  • I tried the flags. Didnt work. The link provided he shows causes but no solutions for my problem. – Qba Oct 25 '21 at 21:05
  • Assuming this is related to permissions, you can try to move your solution outside `C:\Users` directory or run VS with administrative permissions. Does it help? – AndrewSilver Oct 27 '21 at 06:50
  • Not running it in vs. Its console application run with administrator rights – Qba Oct 27 '21 at 08:30
  • 1
    @user2279379 Can you provide more details about the file? Encryption algorithms, PKCS #12 version, type of file protection, ... – kapsiR Oct 27 '21 at 19:06
  • I'd try using built-in command line for certutil -importPFX to see if it works on Server 2012. My money is on something used not supported by Win8/Svr2012, like the github link above @Hazrelle provided, paraphrasing a guess from there: "The PFX was generated using the guidance from rfc7292 to use PBES2+PBKDF2 for encrypting the private keys instead of pbeWithSHAAnd3-KeyTripleDES-CBC. In this case, the PFX will open on Windows 10, but not prior versions (based on recollection)." – Steven Bone Oct 27 '21 at 20:44
  • On the windows server have you checked/logged the path just to ensure the file is actually found? – AliK Oct 28 '21 at 06:42
  • Yes I did, if i type wrong password I got error saying the password is wrong. – Qba Oct 28 '21 at 08:06
  • You probably already looked at this question and the answers, but just in case: https://stackoverflow.com/questions/15003628/x509certificate2-makes-iis-crash – Kevin Oct 28 '21 at 08:19
  • Did you check `initialization phase establishes eleven state variables based upon the nine inputs` – Nadeem Taj Oct 29 '21 at 05:45
  • Tried: new X509Certificate2(fileName, keyPassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable) Didnt work. – Qba Oct 29 '21 at 13:33
  • Have you had a look at: https://github.com/dotnet/runtime/issues/23437 – charliezz10 Oct 29 '21 at 15:54
  • Yes none of answers helepd me out. – Qba Oct 29 '21 at 16:10
  • @user2279379 Above you stated you could provide the .p12 file? If **it is non-sensitive** for you, that would be helpful – kapsiR Oct 31 '21 at 19:52

4 Answers4

4

If your code is running in a web application under IIS:

  1. Go to IIS Manager
  2. Go to the application pool instance
  3. Click advanced settings
  4. Under Process model, set Load User Profile to true

Else, try specifying the UserKeySet (it's possible that the PFX contains the "use the machine store" marker internally):

var path = args[0]; var password = args[1]; var certificate2 = new X509Certificate2(path, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet);

Else, install the certificate on the local machine and try to load from store via thumbprint:

If the above fails, then the .p12 file probably cannot be imported into Windows 2012 using the built-in Windows 2012 tools. To check that: "For each of your PKCS #12 files, you could try the following: issue the command certutil -asn | findstr /i "pb aes des sha" (replacing "" with the name of the PKCS #12 file).

If the output starts like:

| | | | | ; 1.2.840.113549.1.12.1.3 szOID_PKCS_12_pbeWithSHA1And3KeyTripleDES

then it should be possible to import the PKCS #12 file into Windows 2016.

If the output starts like:

| | | | | ; 1.2.840.113549.1.5.13 szOID_PKCS_5_PBES2 | | | | | | ; 1.2.840.113549.1.5.12 szOID_PKCS_5_PBKDF2 | | | | | ; 2.16.840.1.101.3.4.1.42 aes256

or similar, then the PKCS #12 file probably cannot be imported into Windows 2016 using the built-in Windows 2016 tools. You will have to recreate the PKCS #12 file using TripleDES and SHA1." - see thread: https://learn.microsoft.com/en-us/answers/questions/518605/importing-a-pkcs12-to-windows-server-2016.html

Danut Radoaica
  • 1,860
  • 13
  • 17
  • My code is not running under IIS. Its console application. – Qba Oct 25 '21 at 08:09
  • @user2279379 did you try the second approach from above? – Danut Radoaica Oct 25 '21 at 18:54
  • yes I did, didnt work – Qba Oct 25 '21 at 21:02
  • did you try to pass the .pfx bytes directly (not via Chilkat.Cert): var certificate2 = new X509Certificate2(System.IO.File.ReadAllBytes(path), password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet); – Danut Radoaica Oct 26 '21 at 08:07
  • Its .p12 file. I did pass it using your code and didnt work. – Qba Oct 26 '21 at 13:54
  • @user2279379 see try 3 from above – Danut Radoaica Oct 26 '21 at 15:49
  • Have you tried to import the certificate using the GUI certmgr.msc ? Does it work on the server ? If you have checked the "Include all certificates in the certification path if possible" when exporting the private key, I would suggest to export it once again (from Windows 10 if needed) without this option and to choose the `TripleDES-SHA1` encryption as suggested by this answer. – Hazrelle Oct 28 '21 at 08:22
  • When trying to import cert using GUI certmgr.msc I`m getting error: The password is incorrect on windows server 2012. While on Windows 10 everything is fine. – Qba Oct 29 '21 at 16:20
  • Ok solution 3 worked out. My output was: |||||; 1.2.840.113549.1.5.13 szOID_PKCS_5_PBES2 I recreated certificate using link in your link: https://kb.globalscape.com/Knowledgebase/11040/Converting-an-Incompatible-PKCS12-Format-File-to-a-Compatible-PKCS12 It worked out. Is there any option to use C# to do it instead of openssl.exe? – Qba Oct 29 '21 at 16:38
  • Glad it was of help. I do not know of any way to programmatically convert the .p12 to a Windows Server 2012 compatible .p12 – Danut Radoaica Oct 29 '21 at 16:53
0

Have you tried to pass byte data into X509Certificate2 instead of passing path? check below src from the official document.

I have faced a similar problem when loading from HTTP call. I had to pass cert.GetRawCertData() (Not similar to your case, but seems cause is similar)

using System;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.IO;
using System.Security.Cryptography.X509Certificates;

class CertInfo
{
    //Reads a file.
    internal static byte[] ReadFile (string fileName)
    {
        FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read);
        int size = (int)f.Length;
        byte[] data = new byte[size];
        size = f.Read(data, 0, size);
        f.Close();
        return data;
    }
    //Main method begins here.
    static void Main(string[] args)
    {
        //Test for correct number of arguments.
        if (args.Length < 1)
        {
            Console.WriteLine("Usage: CertInfo <filename>");
            return;
        }
        try
        {
            X509Certificate2 x509 = new X509Certificate2();
            //Create X509Certificate2 object from .cer file.
            byte[] rawData = ReadFile(args[0]);
            x509.Import(rawData);

            //Print to console information contained in the certificate.
            Console.WriteLine("{0}Subject: {1}{0}", Environment.NewLine, x509.Subject);
            Console.WriteLine("{0}Issuer: {1}{0}", Environment.NewLine, x509.Issuer);
            Console.WriteLine("{0}Version: {1}{0}", Environment.NewLine, x509.Version);
            Console.WriteLine("{0}Valid Date: {1}{0}", Environment.NewLine, x509.NotBefore);
            Console.WriteLine("{0}Expiry Date: {1}{0}", Environment.NewLine, x509.NotAfter);
            Console.WriteLine("{0}Thumbprint: {1}{0}", Environment.NewLine, x509.Thumbprint);
            Console.WriteLine("{0}Serial Number: {1}{0}", Environment.NewLine, x509.SerialNumber);
            Console.WriteLine("{0}Friendly Name: {1}{0}", Environment.NewLine, x509.PublicKey.Oid.FriendlyName);
            Console.WriteLine("{0}Public Key Format: {1}{0}", Environment.NewLine, x509.PublicKey.EncodedKeyValue.Format(true));
            Console.WriteLine("{0}Raw Data Length: {1}{0}", Environment.NewLine, x509.RawData.Length);
            Console.WriteLine("{0}Certificate to string: {1}{0}", Environment.NewLine, x509.ToString(true));
            Console.WriteLine("{0}Certificate to XML String: {1}{0}", Environment.NewLine, x509.PublicKey.Key.ToXmlString(false));

            //Add the certificate to a X509Store.
            X509Store store = new X509Store();
            store.Open(OpenFlags.MaxAllowed);
            store.Add(x509);
            store.Close();
        }
        catch (DirectoryNotFoundException)
        {
               Console.WriteLine("Error: The directory specified could not be found.");
        }
        catch (IOException)
        {
            Console.WriteLine("Error: A file in the directory could not be accessed.");
        }
        catch (NullReferenceException)
        {
            Console.WriteLine("File must be a .cer file. Program does not have access to that type of file.");
        }
    }
}
cdev
  • 5,043
  • 2
  • 33
  • 32
  • Your code is lacking password argument. Tried with adding password and 0 as flags. No help. – Qba Oct 29 '21 at 16:23
0

Use local computer store for the private key:

X509Certificate2 cert = new X509Certificate2("yourhost.pfx", "password", X509KeyStorageFlags.MachineKeySet);

MachineKeySet is described as private keys are stored in the local computer store rather than the current user store. The default with no flags is to place in the user store.

Even though you are reading the certificate from disk and storing it in an object the private keys are still stored in the Microsoft Cryptographic API Cryptographic Service Provider key database. On the hosting server the ASP.NET process does not have permission to access the user store.

Another approach: (If you change your application console to web)
Modify the IIS Configuration or App Pool identity-- which do work. However, this assumes that there is access to these configuration items which may not be the case (e.g. in shared hosting environment).

You can read more about MSDN.System.Security.Cryptography.X509Certificates

MD. RAKIB HASAN
  • 3,670
  • 4
  • 22
  • 35
0

Setting the MachineKeySet flag will solve the problem of loading the certificate but you'll end up getting "Access denied" error when trying to use it to sign or decrypt, so you will switch to BoucyCastle.

bgman
  • 309
  • 1
  • 5