Getting exception on server when using RSA via CSharp-easy-RSA-PEM - c#

I have used https://github.com/jrnker/CSharp-easy-RSA-PEM for RSA implementation in my mvc project.
It's working fine in my local machine in IIS & also via visual studio but when I deploy my application on server, it gives me below exception.
"System.NullReferenceException: Object reference not set to an instance of an object.\r\n at CoreEntities.Classes.Utility.RSADecrypt(String input)\r\n at WebAPI.Attributes.ApiAuthorizeAttribute.Authorize(HttpActionContext actionContext)"
My code is :
public static string RSADecrypt(string input)
{
try
{
string priv = File.ReadAllText(HostingEnvironment.MapPath("~/Certificates/consumer.pem"));
RSACryptoServiceProvider privc = Crypto.DecodeRsaPrivateKey(priv);
return Crypto.DecryptString(input, privc);
}
catch (Exception ex)
{
throw ex;
}
}
I posted my issue on github also # https://github.com/jrnker/CSharp-easy-RSA-PEM/issues/8
After debugging a lot, I figured out that system is not creating an object of RSACryptoServiceProvider
CspParameters parms = new CspParameters();
parms.Flags = CspProviderFlags.NoFlags;
parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant();
parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1;
// Exception is comping in below line.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms);
RSAParameters rsAparams = new RSAParameters();
Exception:- System.Security.Cryptography.CryptographicException: The system cannot find the file specified.\r\n\r\n at CoreEntities.Classes.Utility.RSADecrypt(String input)\r\n at WebAPI.Attributes.ApiAuthorizeAttribute.Authorize(HttpActionContext actionContext)
Can anyone please help...

#Downvoters, Kindly pay attention.
I found solution to this problem.
This problem is mainly due to the new security constraints that were included into windows server 2008 onwards.
In windows server 2008 a new user with name CryptoGraphic Operator will be created by default.
If your application is using RSACryptoServiceProvider and when you decide to host your application on windows server 2008 IIS7 follow below steps
the account under which the respective application pool of the virtual directory that you create is running should be added in to CryptoGraphic Operator user.
Open IIS7 --> ApplicationPools --> YourAppPool -->RighClikck --> Advanced Settings ---> Load User Profile set this value to true.
This solved my problem.
Ref:- https://social.msdn.microsoft.com/Forums/vstudio/en-US/ec93922a-fd1e-4225-b5cf-1472ebb3acd1/systemsecuritycryptographycryptographicexception-the-system-cannot-find-the-file-specified?forum=netfxbcl

I solved the problem by changing the process Like below and no need to maniupulate the web server (IIS, NGINX or any other). I also tested that on linux, works fine.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xmlPrivateKey);
string toBeSigned= "string";
byte[] signMain = rsa.SignData(Encoding.UTF8.GetBytes(toBeSigned), new SHA1CryptoServiceProvider());
string signature = Convert.ToBase64String(signMain);

Related

Unable to associate an SSL certificate with a binding in IIS programmatically after importing it during an InstallShield installation

I have an InstallShield installation that deploys a website to IIS on a Windows machine. It has always deployed the website with an HTTP binding. I am adding support for deploying the website using HTTPS and I am having some difficulty getting it to work in all cases. I am allowing the user to either select an already installed certificate from the "Personal" or "Web Hosting" stores for the local machine, or select a PFX file from the file system and provide the password. Either way, I obtain the thumbprint of the certificate. As long as the user chooses an SSL certificate that is already in the store, it works as expected. However, if the user chooses to select a PFX file & enter the password, I am able to successfully import the certificate into the "Web Hosting" store, but when I try to add the https binding and associate the certificate to it, I receive the following exception when committing the changes:
"A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)"
The https binding is added successfully, and even if I try to associate the certificate using IIS I get the same message, which tells me it may be related to the Import process more than the Binding process.
I have implemented a handful of methods in a managed library in C# and include the library in the installer project, and created CustomActions in the InstallShield project to invoke these methods. All CustomActions run in Deferred mode in System context, so it has admin rights. I have tons of logging output for troubleshooting purposes, and can see that everything happens without issues, right up to the point that the CommitChanges() method is called when adding the https binding to the website.
Here is my code. I have tried about 10 variations of this as I have found different potential solutions online, but none has been 100% successful. Current state is what I have been most successful with so far (the FindCertificate() method returns a X509Certificate2 object and the name of the store where it was found, and works properly):
/// <summary>
/// Imports a SSL certificate from a file with a password to the specified store on the Local Machine
/// </summary>
/// <param name="customActionData">Semi-colon delimited list containing the certificate path/filename, password, and store name</param>
/// <returns>
/// 1=Success; 0=Invalid arguments; -1=Cert file not found; -100=Exception occurred
/// </returns>
public static Int32 ImportCertificateFromFile(string customActionData)
{
var args = customActionData.Split(new char[] { ';' });
if (args.Length != 3)
return 0;
var certFile = args[0].Replace("\\", "\\\\");
var password = args[1];
var storeName = args[2];
if (string.IsNullOrEmpty(certFile) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(storeName))
return 0;
if (System.IO.File.Exists(certFile))
{
try
{
using (var store = new X509Store(storeName, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadWrite);
var certificate = new X509Certificate2(certFile, password);
store.Add(certificate);
return 1;
}
}
catch (Exception ex)
{
return -100;
}
}
return -1;
}
/// <summary>
/// Adds a binding to a website
/// </summary>
/// <param name="customActionData">Semi-colon delimited list containing the website name, IP address, port, host header, protocol, and certificate thumbprint for the binding to add</param>
/// <returns>
/// 1=Success; 0=Invalid arguments; -1=Website not found; -100=Exception occurred
/// </returns>
public static Int32 AddWebsiteBinding(string customActionData)
{
var args = customActionData.Split(new char[] { ';' });
if (args.Length != 6)
return 0;
var siteName = args[0];
var ipAddress = args[1];
var port = args[2];
var hostHeader = args[3];
var protocol = args[4].ToLower();
var thumbprint = args[5];
var ret = 1;
var serverManager = new ServerManager();
var webSite = serverManager.Sites.FirstOrDefault(s => s.Name == siteName);
if (!(webSite is null))
{
try
{
var binding = webSite.Bindings.CreateElement("binding");
binding.Protocol = protocol;
binding.BindingInformation = $"{ipAddress}:{port}:{hostHeader}";
// add SSL certificate if thumbprint was provided
if (!string.IsNullOrEmpty(thumbprint))
{
var (cert, storeName) = FindCertificate(thumbprint);
if (!(cert is null))
{
binding.CertificateHash = cert.GetCertHash();
binding.CertificateStoreName = storeName;
}
}
webSite.Bindings.Add(binding);
serverManager.CommitChanges();
}
catch (Exception ex)
{
ret = -100;
}
}
else
ret = -1;
return ret;
}
The certificates I am using for testing purposes are self-signed certificates generated using IIS on the machine where the testing is being done, and then the certificates wer exported to .pfx files and given a password. I have tried creating certificates in "Personal" and "Web Hosting" stores, but I don't see any difference once exported to a .pfx file and imported into the "Web Hosting" store.
I am not sure if this will solve your issue, but I have encountered the same error in some code we have for setting up our local dev environments' websites with certs through an app to get us started quickly. It appeared to be because our certificates were not set up correctly when creating the websites. In my case I had to delete the cert first through certlm to make sure the new one was imported in code properly.
Then, in our code to import certificates, we set the key storage flags in the following way (assuming your certificate was created with the option to allow it to be exported):
// Certificate needs to be exportable with the private key to import into IIS
var keyStorageFlags = X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet;
// Store location and key storage flag need to match to import into IIS - this is for our developer environments, probably not for your installshield scenario
if (storeLocation == StoreLocation.LocalMachine)
{
keyStorageFlags |= X509KeyStorageFlags.MachineKeySet;
}
It seems that it would be investigating these X509KeyStorageFlags enum options for your scenarios...this worked for our pfx files, but yours may not have been created with the same settings.

LDAP signing and channel binding for the march 10th update in c# .net

I have been looking into how these two new settings will effect with our c# code that connects to an ldap server and performs user lookups
Using the code below to connect to an AD i have found a few scenarios that no longer work when these settings are switched on.
private static LdapConnection ConnectAndBind(
string server,
int port,
int timeout,
string userName,
string pwd,
AuthType authType,
bool useSSL,
bool useV3)
{
var con = new LdapConnection(new LdapDirectoryIdentifier(server, port));
if (useSSL)
{
con.SessionOptions.SecureSocketLayer = useSSL;
con.SessionOptions.VerifyServerCertificate = VerifyServerCertificate;
con.SessionOptions.QueryClientCertificate = QueryClientCertificate;
}
con.Timeout = new TimeSpan(0, 0, timeout);
con.SessionOptions.ProtocolVersion = useV3 ? 3 : 2;
try
{
con.AuthType = authType;
con.Credential = new NetworkCredential(userName, pwd);
con.Bind();
}
catch (Exception e)
{
throw new ProviderException(
ProviderException.ErrorIdentifier.AuthenticationFailed,
LanguageLogic.GetString("AuthenticationProvider.ConnectError"),
e.Message);
}
return con;
}
This is used in the context of a webforms/mvc asp.net (4.5) app once connected its used to import user details in the the app
but at the moment depending on how the registry keys for the two settings on the AD server are set i am finding some situations where it does not connect (the error returned is that the supplied credentials are invalid)
The first two tables are kinda how i expected it to work with non signed/non ssl basic bind not working
how ever i cannot find a reason why when the Channel binding is set to required (table 3) it does not work for the other 3 red entries
Has any one else been working on this that could shed some light on the matter. would newer version of .net support this setting.
Thanks for any info
UPDATE 1
so i downloaded Softerra LDAP browser. i get the same results using that . so i dont think its my code.
as soon as i turn on the reg key for Channel Binding i get the specified credentials are invalid for those connection methods over SSL
i have updated the AD server with all the latest patches but no change.

PlatformNotSupportedException when establishing a TCP connection

After upgrading to Visual Studio 2019 I found that my existing Xamarin application wasn't able to establish TCP connections anymore.
After some research I found that Visual Studio 2019 also came with MSBuild 16 when VS2017 had MSBuild 15.
before I was using this piece of code after generating a certificate and key pair for my RSA system cryptography:
var cert = new X509Certificate2(DotNetUtilities.ToX509Certificate(bcCert))
{ PrivateKey = DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)keyPair.Private) };
With Visual Studio 2019 you can't set the private key. This can be seen in the decompiled X509Certificate2.cs in
C:\ProgramFiles(x86)\MicrosoftVisualStudio\2019\Professional\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\MonoAndroid\v1.0\System.dll
Following this repo I found a ConvertBouncyCert method that would return an X509Certificate2 wrapper around my generated X509Certificate which would have the private key attached but I was still getting an unable to decode exception.
Jeremy's library contains this code for converting an X509Certificate into an X509Certificate2:
var pkcs12Store = new Pkcs12Store();
var certEntry = new X509CertificateEntry(BouncyCert);
pkcs12Store.SetCertificateEntry(BouncyCert.SerialNumber.ToString(), certEntry);
pkcs12Store.SetKeyEntry(BouncyCert.SerialNumber.ToString(),
new AsymmetricKeyEntry(KeyPair.Private), new[] { certEntry });
X509Certificate2 keyedCert;
using (MemoryStream pfxStream = new MemoryStream()) {
pkcs12Store.Save(pfxStream, null, new SecureRandom());
pfxStream.Seek(0, SeekOrigin.Begin);
keyedCert = new X509Certificate2(pfxStream.ToArray(), "", X509KeyStorageFlags.Exportable);
}
return keyedCert;
You need to create a Pkcs12StoreBuilder object and set the DER encoding to true.
var pkcs12StoreBuilder = new Pkcs12StoreBuilder();
pkcs12StoreBuilder.SetUseDerEncoding(true);
var pkcs12Store = pkcs12StoreBuilder.Build();
var certificateEntry = new X509CertificateEntry(bouncyCert);
This article does a great job at explaining DER and all the components around it within cryptography and RSA systems.

System.Security.Cryptography.CryptographicException: Object already exists

It's weird. I had this method to encrypt a string:
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Assert, Unrestricted = true)]
public static string Encrypt(this string stringToEncrypt, string key) {
var cspp = new CspParameters {
KeyContainerName = key,
Flags = CspProviderFlags.UseMachineKeyStore
};
var rsa = new RSACryptoServiceProvider(cspp) {
PersistKeyInCsp = true
};
var bytes = rsa.Encrypt(System.Text.Encoding.UTF8.GetBytes(stringToEncrypt), true);
return BitConverter.ToString(bytes);
}
And this was my client:
private const string EncryptionKey = "pezhman";
static Random random = new Random();
public static int CreateSalt() {
return random.Next(1000, 9999);
}
public void EncryptSomething() {
var salt = CreateSalt();
var plainText = salt + "," + DateTime.Now;
var encryptionSaltKey = EncryptionKey + DateTime.Now.Date;
// here im calling encryptor:
var encryptedValue = plainText.Encrypt(encryptionSaltKey);
}
I was using this in an ASP.NET MVC 4 application. It was working perfectly; but suddenly it stopped working. Actually, in local, I have no problem and it works. But, when I publish my code to the server, I get this error:
System.Security.Cryptography.CryptographicException: Object already
exists.
Do you have any idea what's happening here? I know I can grant access to the key to everyone. What I'm asking is, what just happened at the server? What is changed? What kind of changes can cause the problem?
What I'm asking is, what just happened at the server? What is changed? What kind of changes can cause the problem?
One possibility is the recently released Windows secuirty update MS14-059, although I can't explain the error message you are getting.
Basically, that update completely uninstalls MVC 4.0.0.0 and replaces it with 4.0.0.1 on your server, and it has caused grief for many people with broken builds. Since cryptography might depend on something very specific to the version number of the DLL, you might want to start there. You can prove or disprove this theory by testing your application on a machine without the above security patch installed to see if it starts working again.

Problems with RSACryptoServiceProvider

I have some problems with RSACryptoServiceProvider on my machine. If a create a new instance:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
I already get an exception, under CspKeyContainerInfo property :
Exportable 'rsa.CspKeyContainerInfo.Exportable' threw an exception of type 'System.Security.Cryptography.CryptographicException' bool {System.Security.Cryptography.CryptographicException}
base {"Key does not exist.\r\n"} System.SystemException {System.Security.Cryptography.CryptographicException}
If I try the same code on the another PC, everything works fine. Are there some settings on my PC that I can check to see if both of them are configured properly and in what the configuration defers? Any clue on what may be the problem?
Thanks
EDIT:
It seem's that it only happens on framework 4.0. Any clue?
I managed to make it work.
CspParameters parms = new CspParameters();
parms.Flags = CspProviderFlags.NoFlags;
parms.KeyContainerName = Guid.NewGuid().ToString().ToUpperInvariant();
parms.ProviderType = ((Environment.OSVersion.Version.Major > 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor >= 1))) ? 0x18 : 1;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(parms);
I do not know why, but it is just like that.
If anyone know why of this behavior, and comment is more then welcome.
Thanks
You can get the provider type in the windows registry:
\local_machine\software\Microsoft\Cryptography\Defaults\Provider\
Look for the provider you want and check the Type value...

Categories

Resources