Using X509Certificate2 to get PrivateKey causes CryptographicException "Invalid provider type specified" - private-key

everyone,
I am developing a web application that uses X509Certificate2 to get a private key from a certification file. Code snippet looks like following:
public static RSACryptoServiceProvider GetSignProviderFromPfx()
{
var strFileName = "c:\cer\mycerfile.pfx";
var strPassword = "000000";
X509Certificate2 pc = new X509Certificate2(strFileName, strPassword, X509KeyStorageFlags.MachineKeySet);
var ThePivateKey = pc.PrivateKey;
return (RSACryptoServiceProvider)ThePivateKey;
}
But the statement pc.Privatekey causes a System.Security.Cryptography.CryptographicException "Invalid provider type specified" . I'm sure the certification file has no problem, it really has a private key. And the property pc.HasPrivateKey is also return true.
The test environment is VS2013, window 7.
I also tried following:
a. I debugged it in VS2013 with iis express, the problem occured.
b. I debugged it in another computer with same enviroment with mine, the problem occured too.
c. I published the application to a server with iis running on Windows Web Server 2008 R2, it worked fine.
d. I published the application to widows azure website, it also worked fine.
Therefore, I guess the code snippet has no problem. The key reason raising the exception is that there may be some problem about running environment. I checked and compared the reading/writing right on the certification file in different environment, all of them are same.
Anybody can help?
Thanks.

I have been having the same problem and this is what I did to solve it. Hopefully this will help you too.
We had to set Load User Profile to True in the app pools advanced settings
You can also set it in the web.config I believe.
https://blogs.msdn.microsoft.com/vijaysk/2009/03/08/iis-7-tip-3-you-can-now-load-the-user-profile-of-the-application-pool-identity/

may be it useful to you;
On local try:
X509Certificate2 pc = new X509Certificate2(strFileName, strPassword);

Related

Directory.CreateDirectory failed on Remote Server

I am working on a small project, in asp.net mvc3, that would copy the deployment files from a local drive to a share drive on a window server 2008 R2 server. I am connected using WMI, and the connection is successful. However, I tried to create a folder, and I receive the message "Logon failure: unknown user name or bad password." Here is a sample code:
bool isConnected = false;
options.Username = user.Name.Trim();
options.Password = user.password.Trim();
mScope = new ManagementScope("\\\\xxx.xxx.xxx.xxx\\root\\cimv2", options);
mScope.Connect();
if (mScope.IsConnected == true)
{
//I've gotten to this point. Then, the code below throw the exception
Directory.CreateDirectory(#"\\\\xxx.xxx.xxx.xxx\Tester\shareFile.txt");
isConnected = true;
}
I'd like to know what am I doing? Is that the right way of doing it?
it is the correct way however it will be the current user you are trying to access that gets passed to the remote computer to create the directory. The management scope at this point has nothing to do with Directory.CreateDirectory. These are 2 different "worlds". you do give the creds to ManagementScope but this has no affect on Directory.CreateDirectory. you could use impersonation to do what you are wanting to:
How do you do Impersonation in .NET?
it is unclear though if you are doing this in ASP.NET/MVC or a different platform. your tags indicate ASP.NET MVC but not your main question.
remember, if you are using ASP.NET/MVC, the credentials of the app pool are being used to perform such actions.

GPG encryption in an ASP.NET C# Web application - No Public Key

I have set-up Gpg4win on Windows Server 2008 R2 and the website is running .Net 4.5.
I'm using the Starksoft OpenPGP dll.
I've added the required public key to Gpg4win via remote desktop, however when testing in the browser I get the following in the browser:
gpg: EMAIL#EMAIL.COM: skipped: No public key gpg: [stdin]: encryption failed: No public key
I've tested locally on my machine and directly in GPA and Kleopatra on the server and the encryption is working correctly. This leads me to believe that the issue is with the public key being set-up via remote desktop and not being accessible to the application pool or similar.
I've tried copying the pubring.gpg, secring.gpg and trustdb.gpg in to a protected subfolder of the website as suggested somewhere (I forget where now) but this has not worked.
Any ideas how to set-up the public keys to be accessible to the IIS user?
Research
Continued research lead me to this SO question: Gpg encryption over web browser which then lead me down the lines of running it via cmd - Running Command line from an ASPX page, and returning output to page
Solution
Export the required keys somewhere (in this case c:\public.key
Create a page with the following code and execute it
System.Diagnostics.Process si = new System.Diagnostics.Process();
si.StartInfo.WorkingDirectory = "c:\\";
si.StartInfo.UseShellExecute = false;
si.StartInfo.FileName = "cmd.exe";
si.StartInfo.Arguments = "gpg --import c:\\public.key";
si.StartInfo.CreateNoWindow = false;
si.StartInfo.RedirectStandardInput = true;
si.StartInfo.RedirectStandardOutput = true;
si.StartInfo.RedirectStandardError = true;
si.Start();
string output = si.StandardOutput.ReadToEnd();
si.Close();
The key now works :)
GnuPG looks for keyrings in user's home directory, and IIS is run by other user, most likely this is the reason. You can specify the exact path to public and secret keyrings via --keyring and --secret-keyring command line switches.

Authentication Issue when accesing Reporting Service

Well, I already tried a lot of stuff to solve this issue, but none did.
I developed a Reporting Service (2005) and deployed it.
This report will be used by everyone who access a website (it's a internet site, so, won't be accessed by intranet) developed on the framework 3.5 (but I think the framework's version is not the source of the problem).
When the user clicks on the button to download the .pdf which the Reporting automatically generates (the end-user never sees the html version of the Report), it asks for windows credentials.
If the user enters a valid credential (and this credential must be a valid credential on the server which the Reporting Service is deployed), the .pdf is obviously downloaded.
But this can't happen. The end-user must download the .pdf directly, without asking for credentials. Afterall, he doesn't even have the credentials.
Response.Redirect("http://MyServer/ReportServer/Pages/ReportViewer.aspx?%2fReportLuiza%2fReportContract&rs:Format=PDF&NMB_CONTRACT=" + txtNmbContractReport.Text);
The code snippet above, shows the first version of my code when the user clicks the button. This one propmts for the Windows credentials.
I already tried to change on IIS the Authentication of the virtual directory ReportServer, but the only one which works is the Windows Credentials. The other ones doesn't even let me open the virtual directory of the Report or the Report Manager's virtual directory.
When I tried to change it to Anonymous Authentication he couldn't access the DataBase. Then I choose the option to Credentials stored securely on the report server. Still doesn't work.
The physical directory of my ReportServer virtual directory points to the reporting server folder on the Hard Disk (C:\Program Files\Microsoft SQL Server\MSSQL.5\Reporting Services\ReportServer). I moved the same folder to my wwwroot directory.
Didn't work. The virtual directory didn't even open. Then I read this could be a problem because I had the same name on two folders (one in C: and other in wwwroot). So I changed the name of the one in wwwroot. Same issue of the DataBase connection couldn't be done.
I returned the physical path to C:
Below, is the second version of my button's event code:
ReportExecutionService rs = new ReportExecutionService();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
rs.Url = "http://MyServer/ReportServer/ReportExecution2005.asmx";
// Render arguments
byte[] result = null;
string reportPath = "/ReportLuiza/ReportContract";
string format = "PDF";
// Prepare report parameter.
ParameterValue[] parameters = new ParameterValue[1];
parameters[0] = new ParameterValue();
parameters[0].Name = "NMB_CONTRACT";
parameters[0].Value = txtNmbContractReport.Text;
string encoding;
string mimeType;
string extension;
Warning[] warnings = null;
string[] streamIDs = null;
ExecutionInfo execInfo = new ExecutionInfo();
ExecutionHeader execHeader = new ExecutionHeader();
rs.ExecutionHeaderValue = execHeader;
execInfo = rs.LoadReport(reportPath, null);
rs.SetExecutionParameters(parameters, "pt-br");
String SessionId = rs.ExecutionHeaderValue.ExecutionID;
try
{
result = rs.Render(format, null, out extension, out encoding, out mimeType, out warnings, out streamIDs);
execInfo = rs.GetExecutionInfo();
}
catch (SoapException se)
{
ShowMessage(se.Detail.OuterXml);
}
// Write the contents of the report to an pdf file.
try
{
using (FileStream stream = new FileStream(#"c:\report.pdf", FileMode.Create, FileAccess.ReadWrite))
{
stream.Write(result, 0, result.Length);
stream.Close();
}
}
catch (Exception ex)
{
ShowMessage(ex.Message);
}
For this code, I had to add a WebReference to the .asmx file mentioned in it.
When I'm debugging (on Visual Studio 2010), the code above works fine, doesn't asking for credentials (unfortunately, it doesn't prompt the option to open, save or cancel de file download. But this is another problem, no need to worry with it now) and save the file on C:.
When published, the code doesn't work. An erros says: The permission granted to user 'IIS APPPOOL\ASP.NET v4.0' are insuficient for performing this operation. So I added to the Reporting Service's users this user. When I tried again, the error is: Login failed for user IISAPPPOOL\ASP.NET v4.0. Cannot create a connection to data source 'MyDataSourceName'.
Both Report and WebSite are deployed/published on the same server with a IIS 7.5 version.
Summarizing: I need a solution where there is no credential prompt, and the user can choose where it wants to save the .pdf file.
Any help will be appreciated.
If you need more information to help me, just ask.
Thanks in advance.
One solution would be to create a new App Pool with an account that has the rights to access your restricted resources and then assign your web application to it.

Starting new process from ASP.NET fails

I'm trying to start a new process from my WCF Service. For that purpose I use
var process = Process.Start(
new ProcessStartInfo { WorkingDirectory = config.WorkingDirectory,
FileName = config.WorkingDirectory,
Arguments = string.Format("{0} {1}", mpcName, jobId),
CreateNoWindow = false,
WindowStyle = ProcessWindowStyle.Hidden });
The WebApp is using a separate AppDomain whose Identity is set to a user account having administrator rights on the server.
Process.Start throws an exception telling
Server execution failed, at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
I also tested setting user and password in ProcessStartInfo. Specifying the password was quite tricky (SecureString) and then I received
The stub received bad data, at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
so I skipped this way.
Do you know what is the reason for my problem and how I can fix it.
I forgot: I'm using Windows Server 2008 R2, IIS 7
I got it!
It's very strange but the only change needed was to invoke
Process.Start(exeFullPath, args);
Obviously the combination of ProcessStartInfo props is important.
This q/a helped me fix this issue in one of my projects, but different cause --
was trying to start process as a Domain user from an integration test being run by nCrunch. Turns out MY problem was a really long argument string.
(same argument string works with no user/password)
Environment is Windows 8, 64 bit.
Anyway, just gonna have to pass the arg data a different way.

Certificate stored in cert store disappearing from ASP.NET application over time

I've got a code signing certificate from Thawte that I'm using to dynamically generate and sign ClickOnce deployment manifests. The problem is that the ASP.NET application that generates and signs these manifests works fine when we restart IIS or re-import the signing certificate, but over time the following function fails to find the certificate:
private static X509Certificate2 GetSigningCertificate(string thumbprint)
{
X509Store x509Store = new X509Store(StoreLocation.CurrentUser);
try
{
x509Store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection x509Certificate2Collection = x509Store.Certificates.Find(
X509FindType.FindByThumbprint, thumbprint, false);
if (x509Certificate2Collection.Count == 0)
throw new ApplicationException(
"SigningThumbprint returned 0 results. Does the code signing certificate exist in the personal store?",
null);
if (x509Certificate2Collection.Count > 1)
throw new ApplicationException(
"SigningThumbprint returned more than 1 result. This isn't possible", null);
var retval = x509Certificate2Collection[0];
if(retval.PrivateKey.GetType() != typeof(RSACryptoServiceProvider))
throw new ApplicationException("Only RSA certificates are allowed for code signing");
return retval;
}
finally
{
x509Store.Close();
}
}
Eventually the application starts throwing errors that it can't find the certificate. I'm stumped because I think the cert is installed correctly (or mostly correct) because it does find the cert when we start the ASP.NET application, but at some point we hit the Count==0 branch and it's just not true: the cert is in the application pool user's "Current User\Personal" cert store.
Question: Why might a cert all of a sudden "disappear" or not be able to be found?
Figured it out on our own (painfully).
The certificate needed to be installed in the LocalMachine store and the application pool account read permissions to the cert using WinHttpCertCfg or CACLS.exe if it's going to be used from an ASP.NET application. Using the CurrentUser store of the account running the application pool was causing the problem. I'm guessing there's some sort of race condition or something that's not entirely cool about accessing the CurrentUser store from a user that isn't running in a interactive logon session.
We were unable to do this at first because we were invoking the MAGE tool to do the ClickOnce deployment manifest creation/signing, and that requires the code signing cert to be in the CurrentUser\My store. However, we've eliminated the need for MAGE by a) creating the manifest from a template file and replacing the values we need to substitute out and b) by signing the manifest by calling the code MAGE calls via reflection that exists in the BuildTasks.v3.5 DLL. As a result we have more control over what cert we use to sign the manifest and can put it wherever we want. Otherwise we'd be stuck had we not gone a little "lower level".
We had the same issue, and it turns out the application pool needed to be set to load the the user profile of the (domain) account that we use to run the application.
If you notice that accessing the My certificate store works while you're signed in with the application pool's user, but not if you were not signed in while the apppool spun up, then this might be the cause for you, too.

Resources