ASP.NET C# page with SSL and Mutual Auth from smartcard - c#

I would like create a login page for user with smartcard authentication and redirect to third party service. I have verified in java and it's work (but dont'know how).
My Https Page have to read certificate with Request.ClientCertificate
after i need to create a webrequest (a Soap Envelope) to 3th party services.
When i execute this code on IIS Express locally , all works (private key of certificate is present)
when i publish on remote IIS this code don't work , the message i receive is
"Authorization Required" (private key of certificate is not present)
This the code is use:
public HttpWebRequest CreateWebRequest(string url)
{
Uri uri = new Uri(url);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
System.Net.ServicePointManager.ServerCertificateValidationCallback += mIgnoreBadCertificates;
webRequest.Proxy = null;
webRequest.Headers.Add("SOAP:Action");
webRequest.KeepAlive = true;
webRequest.ProtocolVersion = HttpVersion.Version10;
webRequest.Headers.Add("X-WASP-User", DataOnCertifcate);
webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.Accept = "text/xml";
webRequest.Method = "POST";
webRequest.AuthenticationLevel = AuthenticationLevel.MutualAuthRequired;
if (Request.ClientCertificate != null)
{
webRequest.ClientCertificates.Clear();
webRequest.ClientCertificates.Add(Request.ClientCertificate);
}
return webRequest;
}
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
HttpWebRequest request = CreateWebRequest(Url);
using (Stream stream = request.GetRequestStream())
{
envelope.Save(stream);
}
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
IAsyncResult asyncResult = request.BeginGetResponse(null, null);
asyncResult.AsyncWaitHandle.WaitOne();
string soapResult = "";
using (WebResponse webResponse = request.EndGetResponse(asyncResult))
using (StreamReader rd = new StreamReader(webResponse.GetResponseStream()))
{
soapResult = rd.ReadToEnd();
}
1) on IIS there is SSL enabled
2) When i access to my https page , browser show me a list of certificate.
3) I choice the certificate present into smartcard, and i insert the Pin corretly
4) my https test page , show me the correct certificate and public key information, but no private key information (on remote Server, meanwhile on local server all works)
5) i Get this error
401 Authorization Required Authorization Required This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required. Apache/2.2.3 (Red Hat) Server at server name Port 443
How can i fix this problem?

Related

WCF SSL .pfx Binding

My API call another secure API internally below is my code :
string ID = Convert.ToString(ConfigurationManager.AppSettings["ID"]);
string Password = Convert.ToString(ConfigurationManager.AppSettings["Password"]);
string client_ID = Convert.ToString(ConfigurationManager.AppSettings["client_ID"]);
string client_secret = Convert.ToString(ConfigurationManager.AppSettings["client_secret"]);
string patchURI = Convert.ToString(ConfigurationManager.AppSettings["SecureApi"]);
patchURI = patchURI + "?client_ID=" + client_ID + "&client_secret=" + client_secret;
string JsonStringParams = jsonStringRequest();
//ServicePointManager.Expect100Continue = true;
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
byte[] data = Encoding.UTF8.GetBytes(JsonStringParams);
var request = (HttpWebRequest)WebRequest.Create(patchURI);
request.KeepAlive = false;
request.Method = "POST";
request.ContentType = "application/json";
request.ContentType = "application/x-www-form-urlencoded";
request.Headers.Add("UserName", LDAPID);
request.Headers.Add("Password", Password);
string requestData = JsonStringParams;
data = Encoding.UTF8.GetBytes(requestData);
Stream dataStream = request.GetRequestStream();
dataStream.Write(data, 0, data.Length);
dataStream.Close();
var response = request.GetResponse(); // failed at this stage Error : The request was aborted: Could not create SSL/TLS secure channel.",
string result = new StreamReader(response.GetResponseStream()).ReadToEnd();
result = new StreamReader(response.GetResponseStream()).ReadToEnd();
my patchURI contains a https link which needs a .pfx SSL and client has already same .cer/crt installed
When i use the patchURI and pass SSL in SOAPUI Tool i get response from Client server, but when i use through this code it fails and generates and err : - Failed to Create SSL Channel.
The error typically indicates that the trust relationship has not been established yet. In order to establish the relationship between the client-side and the server-side, we should install each other’s certificate in the TRUSTED ROOT CERTIFICATION AUTHORITIES store.
It is not recommended to call the WCF service by using an Http client library except that the WCF service created by WebHttpbinding(Restful WCF service). We usually call the WCF soap web service by using a client proxy. The below code can be used for manually verifying the certificate while sending an Http request.
ServicePointManager.ServerCertificateValidationCallback += delegate
{
return true;
};
Besides, please post the complete error message if the error still exists.
Feel free to let me know if there is anything I can help with.

How to encrypt username/password for session

I am making a call to a 3rd party service via https (using HttpWebRequest and sending a username, password in order to return a token which is then needed to make future requests for data). The service would only be required to list items on a public ASPNet website.
There will be no database involved so session or cookies would be storing the token.
To get the token I send a POST request which includes the username/password but I can see these details (username/password) in Fiddler (headers text tab I think but can confirm if anyone asks) - personally I thought I shouldn't? When I make a GET request to get the items I send the token and all works.
So am I supposed to encrypt the username/password somehow before making retrieving the token? If yes how would I do that?
I just feel that anyone could check the POST request and see what's going on. I could be wrong but happy to test any theories.
Edit 1
Here is the code i am sending the POST request. Please note the username and password along with the URL which is https
private string UsernamePassword()
{
string un = new JavaScriptSerializer().Serialize(new
{
User = "abc",
Password = "123"
});
return un;
}
HttpWebRequest wr = (HttpWebRequest)WebRequest.Create("https://site.data.com");
wr.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate, br");
wr.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-GB,en-US;q=0.9,en;q=0.8");
wr.Headers.Add("Sec-Fetch-Site", "same-origin");
wr.Headers.Add("Sec-Fetch-Mode", "cors");
wr.Accept = "application/json";
wr.ContentType = "application/json";
byte[] data = null;
wr.Method = "POST";
data = Encoding.UTF8.GetBytes(UsernamePassword());
wr.ContentLength = data.Length;
wr.KeepAlive = true;
wr.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
try
{
using (Stream stream = wr.GetRequestStream())
{
stream.Write(data, 0, data.Length);
stream.Flush();
stream.Close();
}
using (HttpWebResponse httpResponse = (HttpWebResponse)wr.GetResponse())
{
var encoding = Encoding.GetEncoding(httpResponse.CharacterSet);
#germi is right. That's exactly what TLS/Https is for. The fact that you can see the content of your https request doesn't mean anyone can.
As long as your endpoint is using https (and not http), the exchange will happen over an encrypted channel. If you want to verify, install Wireshark and see for yourself.

Adding .pfx certificate to HttpWebRequest fails from Web application

I have created a Asp.net web application-C#, where I need to send a HttpWebRequest with a .pfx certificate.
I have installed the certificate to the personal certificates folder(added rights for user NETWORK SERVICE) and able to see it in the X509Certificate2Collection and no issues while adding the certificate to the HttpWebRequest.
But it throws cannot connect to server error while fetching the response (HttpWebResponse)req.GetResponse()
I am facing this issue only from web application, the same code works fine(got the correct response) when i tried using a C# console application.
X509Store oLocalMachineStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
X509Certificate2Collection oCertColl;
X509Certificate oCert = new X509Certificate();
oLocalMachineStore.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadOnly);
oCertColl = oLocalMachineStore.Certificates.Find(System.Security.Cryptography.X509Certificates.X509FindType.FindByIssuerName, "mycertificate issuer", false);
oLocalMachineStore.Close();
foreach (X509Certificate oCertFromStore in oCertColl)
{
if (oCertFromStore.Issuer.Contains("mycertificate issuer"))
{
oCert = oCertFromStore;
oCert.Import(#"certlocation\mycertificate.pfx", "pwd", X509KeyStorageFlags.MachineKeySet);
break;
}
}
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("destination URL");
req.ClientCertificates.Add(oCert);
req.UserAgent = "LOL API Client";
req.Accept = "application/json";
req.Method = WebRequestMethods.Http.Get;
string result = null;
using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
{
StreamReader reader = new StreamReader(resp.GetResponseStream());
result = reader.ReadToEnd();
}
The issue got resolved after adding proxy to the http request.
req.Proxy = New Net.WebProxy("ProxyURL", False);
But still i have no idea, how it worked when i tried with the same code from a console application with out adding proxy.
If anyone has any idea on this please share.

Keeping HTTP Basic Authentification alive while being redirected

We are using web service with basic authentication.
It all worked all fine, till owners of web service implemented balancing service.
Which is simply redirects requests to different instances of web service.
The problem is that after being redirected basic authentication fails.
There is "request authentication credentials was not passed" exception.
Additional info:
We have to create request manually.
var req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(Settings.Default.HpsmServiceAddress));
req.Headers.Add("Authorization", "Basic aaaaaaaaaaa");
req.PreAuthenticate = true;
req.AuthenticationLevel = AuthenticationLevel.MutualAuthRequested;
req.UserAgent = "Apache-HttpClient/4.1.1 (java 1.5)";
req.KeepAlive = false;
ServicePointManager.Expect100Continue = false;
req.ContentType = "text/xml; charset=utf-8";
req.Method = "POST";
req.Accept = "gzip,deflate";
req.Headers.Add("SOAPAction", actionName);
byte[] buffer = Encoding.UTF8.GetBytes(envelop);
Stream stm = req.GetRequestStream();
stm.Write(buffer, 0, buffer.Length);
stm.Close();
WebResponse response = req.GetResponse();
string strResponse = new StreamReader(response.GetResponseStream()).ReadToEnd();
response.Dispose();
We are redirected with HTTP 307 redirect
Follow the MSDN for HttpWebRequest.AllowAutoRedirect Property i found this :
The Authorization header is cleared on auto-redirects and
HttpWebRequest automatically tries to re-authenticate to the
redirected location. In practice, this means that an application can't
put custom authentication information into the Authorization header if
it is possible to encounter redirection. Instead, the application must
implement and register a custom authentication module. The
System.Net.AuthenticationManager and related class are used to
implement a custom authentication module. The
AuthenticationManager.Register method registers a custom
authentication module.
Solution is to write a custom Authentication Module.
Here what i've found about it :
http://msdn.microsoft.com/en-us/library/system.net.authenticationmanager.aspx
And here the AllowAutoRedirect properties page :
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.allowautoredirect.aspx
UPDATE
Can you try to use CredentialCache instead of add header to webrequest ?
CredentialCache myCache = new CredentialCache();
myCache.Add(
new Uri("http://www.contoso.com/"),"Basic",new NetworkCredential(UserName,SecurelyStoredPassword));
req.Credentials = myCache;
Indeed, CredentialCache is working correctly. However, if you would like to add multiple basic auth credentials (for example if there is redirection that you are aware of) you can use following function that I have made:
private void SetNetworkCredential(Uri uriPrefix, string authType, NetworkCredential credential)
{
if (request.Credentials == null)
{
request.Credentials = new CredentialCache();
}
if (request.Credentials.GetCredential(uriPrefix, authType) == null)
{
(request.Credentials as CredentialCache).Add(uriPrefix, authType, credential);
}
}
I hope it will help somebody in the future.

FormsAuthenticate through WCF (How do I make the Session apply to browser?!)

Users are authenticating to a REST WCF Service (my own). The credentials are sent through AJAX with Javascript and JSON format. The service reply with a OK and little info (redirect url) to the client, when authenticated.
Now, There are a new method provided for external authentication, and I have to create a compact code snippet that are easy to paste & run inside a asp.net code file method.
A typical wcf request could end up like this,
http://testuri.org/WebService/AuthenticationService.svc/ExtLogin?cId=197&aId=someName&password=!!pwd
My code snippet so far,
protected void bn_Click(object sender, EventArgs e)
{
WebHttpBinding webHttpBinding = new WebHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress(url);
ContractDescription cd =
ContractDescription.GetContract(typeof(IAuthenticationService));
ServiceEndpoint sep = new ServiceEndpoint(cd);
sep.Behaviors.Add(new WebHttpBehavior());
sep.Address = endpointAddress;
sep.Binding = webHttpBinding;
var resp = new ChannelFactory<IAuthenticationService>(sepREST).CreateChannel();
LoginResult result = resp.ExtLogin(cId, aId, hashPwd);
Response.Redirect(result.RedirectUri);
// I.e. http://testuri.org/Profile.aspx (Require authenticated to visit)
}
I recieve correct authenticated reply in the resp/result objects. So, the communication are fine. When redirecting to the actual website, I'm not authenticated. I can't locate the problem? If I take the URI above (with valid credentials) and paste into my Webbrowser URL, and then manually type the uri, i'm authenticated.
I've spent a day searched the net for this, without success.
There are a LOT of info but none seem to apply.
What am I missing?
I also tried another approach but the same problem persist.
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uriWithParameters);
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
request.ContentType = "application/json";
request.Accept = "application/json";
request.Method = "GET";
string result;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
result = reader.ReadToEnd();
JavaScriptSerializer jsonDeserializer = new JavaScriptSerializer();
LoginResult contact = jsonDeserializer.Deserialize<LoginResult>(result);
Response.Redirect(result.RedirectUri);
I'm not sure about this answer, but will offer it anyway as nobody else has posted:
I think it's because the request that has been authenticated is the request sent via code.
When you redirect it's a totally different request - so is still not authenticated.
All authentication techniques require some way of maintaining the authenticated state across 'stateless' requests = session cookies or some kind of authentication token.
Whatever token you get back from the call to the authentication service needs to be available to your website requests as well - dumping the token from the request into a cookie might be an option.
Can you see (in something like Fiddler) an auth token being sent as part of the request to 'RedirectUrl'?

Categories

Resources